具有互斥锁成员的默认移动构造函数
Default move constructor with mutex member
我有一个删除了复制构造函数的类,我正在尝试将互斥成员放入如下所示的内容中:
struct A {
A(const A &other) = delete;
A& operator=(const A &other) = delete;
A(A&& other) = default;
A& operator=(A &&other) = default;
std::mutex lock;
};
编译器抱怨我试图调用已删除的复制构造函数,我总结这是由于 std::mutex 类型是不可移动的。如何让互斥体成员以最小的麻烦与移动构造函数一起玩?我实际上不想将互斥成员本身移动到新构造的对象中,并且实际上希望每个移动的对象都只是构造自己的互斥锁
我实际上不想将互斥成员本身移动到新构造的对象中,并且实际上希望每个移动的对象都只是构造自己的互斥锁
然后只需定义移动构造函数即可构造新的互斥锁:
struct A {
A(const A &other) = delete;
A& operator=(const A &other) = delete;
A(A&& other)
: lock()
{
}
A& operator=(A &&other) = delete;
std::mutex lock;
};
移动分配仍然是一个问题,可能应该删除。除非你能回答这个问题:当你被分配一个新值时,现有的互斥锁成员会发生什么?特别是:如果在锁定现有互斥锁时为您分配了新值怎么办?
作为为类提供自定义移动操作的替代方法,您可以创建一个泛型包装器:
template <class T>
class PerObject
{
T data;
public:
PerObject() = default;
PerObject(const PerObject&) {}
PerObject& operator= (const PerObject&) { return *this; }
T& operator* () const { return data; }
T* operator-> () const { return &data; }
};
并像这样使用它:
struct A {
A(const A &other) = delete;
A& operator=(const A &other) = delete;
A(A&& other) = default;
A& operator=(A &&other) = default;
PerObject<std::mutex> lock;
};
包装器的复制(和移动(操作是无操作的,因此包含包装器的对象将始终包含它开始的对象。
警告:但是,根据类使用互斥锁的方式,上述情况实际上可能是危险的。如果互斥锁用于保护类中的其他数据,则在将对象分配到时,它应该可能被锁定,因此无论如何都必须提供手动移动操作。在这种情况下,代码可能如下所示:
struct A {
A(A&& other) : lock{}, /* anything else you need to move-construct */
{
// Note: it might even be necessary to lock `other.lock` before moving from it, depending on your class's exact semantics and expected use.
}
A& operator=(A &&other)
{
if (this == &other) return *this; // Otherwise, double-locking would be possible
// If you need to lock only this object:
std::unique_lock<std::mutex> l(lock);
// Alternatively, if you need to lock both objects:
std::scoped_lock l(lock, other.lock);
// Now move data from other to this
return *this;
}
std::mutex lock;
};
一种方法是使移动构造函数在调用时创建新mutex
。
A(A&& other): lock()
{
//... move other things
}
您还可以对std::mutex
使用std::unique_ptr()
,因为它是可移动的。
struct A {
A(const A &other) = delete;
A& operator=(const A &other) = delete;
A(A&& other) = default;
A& operator=(A &&other) = default;
std::unique_ptr<std::mutex> lock;
};
A::A() : lock(new std::mutex())
使用此方法,您不会在每次移动对象时创建新的互斥锁,这将消除一些开销。
相关文章:
- 为什么不调用移动构造函数?(默认情况下只有构造器,没有别的)
- 这将执行默认移动操作吗?
- 为什么 std::move 不将默认移动构造函数中的源变量更改为默认值?
- 默认移动成员定义为已删除,而未定义特殊成员?
- 显式默认析构函数禁用类中的默认移动构造函数
- 编译器是否优化了默认移动构造函数?
- POD 类型是否可以具有显式声明的默认移动分配运算符
- 具有互斥锁成员的默认移动构造函数
- 默认移动分配调用析构函数,复制分配不
- 默认移动构造函数采用 const 参数
- 如何使用默认移动构造函数将数据移出范围
- 默认移动构造函数
- 默认移动构造函数 Visual Studio 2015
- 默认移动构造
- 警告:X的默认移动分配运算符将多次移动分配虚拟基类Y
- 了解默认移动构造函数定义
- 子类中的默认移动构造函数
- 被认为是用户声明的默认移动构造函数
- 默认移动构造函数、默认复制构造函数和默认赋值操作符
- 自动生成带有声明析构函数的默认移动构造函数