为什么 class 模板中的成员函数在同一地址创建相同的对象
Why a member function in a class template creates the same object in the same address
我正在测试我最近写的一个无锁工作窃取队列。但是,我发现了一个奇怪的问题,当我将一个新项目推送到队列时,它只会 returns 我最后推送的同一个对象。我花了一天时间,但我仍然不明白为什么会这样。
这是输出,每次,数据都是相同的被推送到队列,这就是为什么我只有最后一个结果是 45,它应该是 45 个 res 项目。任何人都可以在这里帮助我:(
- push_back 地址:0x21d3ea0 bk:0 &数据:0x7ffe36802380
- push_back 地址:0x21d3ea4 bk:0 &数据:0x7ffe36802380
- push_back 地址:0x21d3ea8 bk:0 &数据:0x7ffe36802380
- push_back 地址:0x21d3eac bk:0 &数据:0x7ffe36802380
- 分辨率[0]=-1
- res[1]=-1
- res[2]=-1
- res[3]=45
下面是简化代码:
#include <memory>
#include <functional>
#include <type_traits>
class FunctionWrapper {
private:
class ImplInterface {
public:
virtual void invoke() = 0;
virtual ~ImplInterface() {}
};
std::unique_ptr<ImplInterface> impl;
template <typename F, typename... Args>
class Impl : public ImplInterface {
private:
std::function<void()> callBack;
public:
Impl(F&& f_, Args&&... args_) {
callBack = [&f_, &args_...]() { f_(std::forward<Args>(args_)...); };
}
void invoke() override { callBack(); }
};
public:
template <typename F, typename... Args>
FunctionWrapper(F&& f_, Args&&... args_) : impl(new Impl<F, Args...>(std::forward<F>(f_), std::forward<Args>(args_)...)) {}
void operator()() { impl->invoke(); }
FunctionWrapper() = default;
FunctionWrapper(FunctionWrapper&& other) : impl(std::move(other.impl)) {}
FunctionWrapper& operator=(FunctionWrapper&& other) {
impl = std::move(other.impl);
return *this;
}
FunctionWrapper(const FunctionWrapper&) = delete;
FunctionWrapper(FunctionWrapper&) = delete;
FunctionWrapper& operator=(const FunctionWrapper&) = delete;
};
#include <atomic>
#include <array>
#include <iostream>
#include "functionwrapper.h"
class LockFreeWorkStealingQueue {
private:
using DataType = FunctionWrapper;
static constexpr auto DEFAULT_COUNT = 2048u;
static constexpr auto MASK = DEFAULT_COUNT - 1u;
std::array<DataType, DEFAULT_COUNT> q;
unsigned int ft{0};
unsigned int bk{0};
public:
LockFreeWorkStealingQueue() {}
LockFreeWorkStealingQueue(const LockFreeWorkStealingQueue&) = delete;
LockFreeWorkStealingQueue& operator=(const LockFreeWorkStealingQueue&) = delete;
void push_back(DataType data) {
std::cout << "bk: " << (bk&MASK) << " &data: " << &data << std::endl;
q[bk & MASK] = std::move(data);
bk++;
}
bool try_pop_back(DataType& res) {
if (bk > ft) {
res = std::move(q[(bk - 1) & MASK]);
bk--;
return true;
}
return false;
}
};
#include "lockfreeworkstealingqueue.h"
#include <iostream>
#include <algorithm>
#include <numeric>
#include <cassert>
#include <vector>
constexpr unsigned int NUM = 4;
void sumOver(const std::vector<int>& v, int& res) {
res = std::accumulate(v.begin(), v.end(), 0);
//std::cout << "call sumOver, res = " << res << std::endl;
//std::cout << "call sumOver, addr: " << &res << std::endl;
}
int main () {
std::vector<int> v { 1,2,3,4,5,6,7,8,9 };
std::vector<int> res(NUM, -1);
std::vector<LockFreeWorkStealingQueue> wsq(4);
{
for (auto i = 0; i < 4; ++i) {
for (auto j = 0; j < NUM / 4; ++j) {
std::cout << "push_back addr: " << &res[i*(NUM/4)+j] << std::endl;
wsq[i].push_back(FunctionWrapper(sumOver, std::ref(v), std::ref(res.at(i*(NUM/4)+j))));
}
}
FunctionWrapper f;
for (auto i = 0; i < 4; ++i) {
for (auto j = 0; j < NUM / 4; ++j) {
if(wsq[i].try_pop_back(f)) {
f();
}
}
}
}
for (auto i = 0; i < 4; ++i) {
for (auto j = 0; j < NUM / 4; ++j) {
std::cout << "res[" << i*(NUM/4)+j << "]=" << res[i*(NUM/4)+j] << std::endl;
}
}
return 0;
}
编辑:我对 functionwrapper.h 进行了更改以反映评论。现在效果很好。
#include <memory>
#include <functional>
class FunctionWrapper {
private:
std::function<void()> callback;
public:
template <typename F, typename... Args>
FunctionWrapper(F&& f_, Args&&... args_) : callback([f_, args_...]() { f_(args_...); }) {}
void operator()() { callback(); }
FunctionWrapper() = default;
FunctionWrapper(FunctionWrapper&& other) : callback(std::move(other.callback)) {}
FunctionWrapper& operator=(FunctionWrapper&& other) {
callback = std::move(other.callback);
return *this;
}
FunctionWrapper(const FunctionWrapper&) = delete;
FunctionWrapper(FunctionWrapper&) = delete;
FunctionWrapper& operator=(const FunctionWrapper&) = delete;
};
FunctionWrapper::Impl
中的 lambda 捕获对临时 std::reference_wrapper
实例的引用(由 main
中的 std::ref
调用产生)。到实际调用 lambda 时,那些临时对象早已被销毁并且引用悬空。因此,您的程序通过访问生命周期已结束的对象而表现出未定义的行为。
您想改为按值捕获,如
Impl(F&& f_, Args&&... args_) {
callBack = [f_, args_...]() { f_(args_...); };
}
我正在测试我最近写的一个无锁工作窃取队列。但是,我发现了一个奇怪的问题,当我将一个新项目推送到队列时,它只会 returns 我最后推送的同一个对象。我花了一天时间,但我仍然不明白为什么会这样。 这是输出,每次,数据都是相同的被推送到队列,这就是为什么我只有最后一个结果是 45,它应该是 45 个 res 项目。任何人都可以在这里帮助我:(
- push_back 地址:0x21d3ea0 bk:0 &数据:0x7ffe36802380
- push_back 地址:0x21d3ea4 bk:0 &数据:0x7ffe36802380
- push_back 地址:0x21d3ea8 bk:0 &数据:0x7ffe36802380
- push_back 地址:0x21d3eac bk:0 &数据:0x7ffe36802380
- 分辨率[0]=-1
- res[1]=-1
- res[2]=-1
- res[3]=45
下面是简化代码:
#include <memory>
#include <functional>
#include <type_traits>
class FunctionWrapper {
private:
class ImplInterface {
public:
virtual void invoke() = 0;
virtual ~ImplInterface() {}
};
std::unique_ptr<ImplInterface> impl;
template <typename F, typename... Args>
class Impl : public ImplInterface {
private:
std::function<void()> callBack;
public:
Impl(F&& f_, Args&&... args_) {
callBack = [&f_, &args_...]() { f_(std::forward<Args>(args_)...); };
}
void invoke() override { callBack(); }
};
public:
template <typename F, typename... Args>
FunctionWrapper(F&& f_, Args&&... args_) : impl(new Impl<F, Args...>(std::forward<F>(f_), std::forward<Args>(args_)...)) {}
void operator()() { impl->invoke(); }
FunctionWrapper() = default;
FunctionWrapper(FunctionWrapper&& other) : impl(std::move(other.impl)) {}
FunctionWrapper& operator=(FunctionWrapper&& other) {
impl = std::move(other.impl);
return *this;
}
FunctionWrapper(const FunctionWrapper&) = delete;
FunctionWrapper(FunctionWrapper&) = delete;
FunctionWrapper& operator=(const FunctionWrapper&) = delete;
};
#include <atomic>
#include <array>
#include <iostream>
#include "functionwrapper.h"
class LockFreeWorkStealingQueue {
private:
using DataType = FunctionWrapper;
static constexpr auto DEFAULT_COUNT = 2048u;
static constexpr auto MASK = DEFAULT_COUNT - 1u;
std::array<DataType, DEFAULT_COUNT> q;
unsigned int ft{0};
unsigned int bk{0};
public:
LockFreeWorkStealingQueue() {}
LockFreeWorkStealingQueue(const LockFreeWorkStealingQueue&) = delete;
LockFreeWorkStealingQueue& operator=(const LockFreeWorkStealingQueue&) = delete;
void push_back(DataType data) {
std::cout << "bk: " << (bk&MASK) << " &data: " << &data << std::endl;
q[bk & MASK] = std::move(data);
bk++;
}
bool try_pop_back(DataType& res) {
if (bk > ft) {
res = std::move(q[(bk - 1) & MASK]);
bk--;
return true;
}
return false;
}
};
#include "lockfreeworkstealingqueue.h"
#include <iostream>
#include <algorithm>
#include <numeric>
#include <cassert>
#include <vector>
constexpr unsigned int NUM = 4;
void sumOver(const std::vector<int>& v, int& res) {
res = std::accumulate(v.begin(), v.end(), 0);
//std::cout << "call sumOver, res = " << res << std::endl;
//std::cout << "call sumOver, addr: " << &res << std::endl;
}
int main () {
std::vector<int> v { 1,2,3,4,5,6,7,8,9 };
std::vector<int> res(NUM, -1);
std::vector<LockFreeWorkStealingQueue> wsq(4);
{
for (auto i = 0; i < 4; ++i) {
for (auto j = 0; j < NUM / 4; ++j) {
std::cout << "push_back addr: " << &res[i*(NUM/4)+j] << std::endl;
wsq[i].push_back(FunctionWrapper(sumOver, std::ref(v), std::ref(res.at(i*(NUM/4)+j))));
}
}
FunctionWrapper f;
for (auto i = 0; i < 4; ++i) {
for (auto j = 0; j < NUM / 4; ++j) {
if(wsq[i].try_pop_back(f)) {
f();
}
}
}
}
for (auto i = 0; i < 4; ++i) {
for (auto j = 0; j < NUM / 4; ++j) {
std::cout << "res[" << i*(NUM/4)+j << "]=" << res[i*(NUM/4)+j] << std::endl;
}
}
return 0;
}
编辑:我对 functionwrapper.h 进行了更改以反映评论。现在效果很好。
#include <memory>
#include <functional>
class FunctionWrapper {
private:
std::function<void()> callback;
public:
template <typename F, typename... Args>
FunctionWrapper(F&& f_, Args&&... args_) : callback([f_, args_...]() { f_(args_...); }) {}
void operator()() { callback(); }
FunctionWrapper() = default;
FunctionWrapper(FunctionWrapper&& other) : callback(std::move(other.callback)) {}
FunctionWrapper& operator=(FunctionWrapper&& other) {
callback = std::move(other.callback);
return *this;
}
FunctionWrapper(const FunctionWrapper&) = delete;
FunctionWrapper(FunctionWrapper&) = delete;
FunctionWrapper& operator=(const FunctionWrapper&) = delete;
};
FunctionWrapper::Impl
中的 lambda 捕获对临时 std::reference_wrapper
实例的引用(由 main
中的 std::ref
调用产生)。到实际调用 lambda 时,那些临时对象早已被销毁并且引用悬空。因此,您的程序通过访问生命周期已结束的对象而表现出未定义的行为。
您想改为按值捕获,如
Impl(F&& f_, Args&&... args_) {
callBack = [f_, args_...]() { f_(args_...); };
}