带互斥成员的默认移动构造函数

时间:2019-04-04 13:52:07

标签: c++ move

我有一个带有已删除副本构造函数的类,并且我试图将互斥锁成员放入这样的内容中:

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类型是不可移动的。我如何才能使互斥锁成员与move构造函数一起以最小的麻烦玩呢?我实际上并不希望将互斥锁成员本身移动到新构造的对象中,并且实际上希望每个移动的对象只是构造自己的互斥体

3 个答案:

答案 0 :(得分:5)

  

我实际上并不希望将互斥锁成员本身移动到新构造的对象中,而是实际上希望每个移动的对象只是构造自己的互斥锁

然后只需定义您的move构造函数即可构造一个新的互斥体:

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;
};

移动分配仍然是个问题,应该删除即可。除非您能回答以下问题:在为新的互斥成员分配新值时会发生什么?特别是:如果在锁定现有互斥锁的情况下为您分配新值怎么办?

答案 1 :(得分:5)

除了为您的类提供自定义移动操作之外,您还可以创建一个通用包装器:

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;
};

答案 2 :(得分:1)

一种方法是使您的move构造函数在调用时创建新的mutex

 A(A&& other): lock()
 {
     //... move other things
 }

由于std::unique_ptr()是可移动的,因此您也可以在std::mutex上使用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())

contrib.layers

使用这种方法,您不会在每次移动对象时创建新的互斥体,这将消除一些开销。