将仅移动类型添加到类会使该类成为仅移动类型?

时间:2016-03-29 12:55:57

标签: c++ c++11 mutex c++14 stdatomic

在Scott Meyers的“有效的现代C ++”中,他写道:

  

因为std::mutex是一种只移动的类型(即可以是一种类型的移动类型),所以没有价值   移动,但未复制),将m添加到Polynomial的副作用是Polynomial失去了复制的能力。但是,它仍然可以被移动。

在这里,他将std::mutex m作为成员变量添加到类Polynomial中,解释了为什么应该保护对类的const成员变量的访问(多线程)。我理解他解释的概念。但是我需要更多解释的一件事是“为什么只向类中添加一个移动类型会使该类只能移动类型而不能复制”?为什么类型如std::mutexstd::atomic是默认只移动类型?如果我们想要执行复制操作,即使向我们的类添加仅移动类型的变量,我们如何才能克服它?

3 个答案:

答案 0 :(得分:2)

  

“为什么只将一个移动类型添加到类中会使该类仅移动类型而不能复制”

这不是真实的。添加不可复制的成员变量会阻止您编写类似Type (const Type& t) = default;的内容,但您仍然可以实现自己的复制构造函数,忽略不可复制的成员

class Type{

int integer;
std::mutex m;

public:
Type() = default;
Type(const Type& rhs): integer (rhs.integer){}

}

除此之外,您可以找到许多包含仅移动成员的对象示例,但它们本身是不可移动的,只是因为开发人员删除了移动构造函数。

  

为什么像std :: mutex和std :: atomic这样的类型默认只移动类型?

它们都没有实现移动构造函数。这是一个错误的陈述。

答案 1 :(得分:1)

std::mutex不可复制,因为标准是这样说的。他们可能写了一个可复制的std::mutex,但他们决定不这样做。

作为一种猜测,不可复制的std::mutex可能更有效,更安全或更容易理解。

举个例子,这就是为什么它可能更安全/更容易理解:复制一个被锁定的std::mutex是什么意思?我个人不知道正确的答案是什么;我怀疑正确的答案是“这是无稽之谈”。没有“最不惊讶的答案”。

通过阻止复制而不是在并发代码中有一个令人惊讶的答案,我们避免了这个问题。存储想要复制的互斥锁的人必须自己决定他们想要做什么。

C ++会在某些情况下自动生成复制构造函数。在更多情况下,它会让你MyClass(MyClass const&)=default并在你要求时写一个。

如果您的类包含不可复制的类,则它不会生成一个类,并且您无法请求它。这是因为生成的拷贝构造函数基本上复制了它的成员;如果您无法复制成员,则无法生成复制构造函数。

所以mutex无法复制,因为标准是这样说的。

如果结构或类包含不可复制的成员,则不能使用默认的复制构造函数。你必须自己写一个。

如果您的类型需要存储mutex,一种方法可以防止必须维护手动复制其所有其他成员的副本的麻烦,那就是将非mutex状态粘贴到子结构,然后使用其默认副本。然后,您决定在复制期间如何处理mutex,并且复制ctor(和赋值复制操作符)仍然很简单。

struct bob_with_mutex {
  sturct bob_simple_data {
    int x, y, z;
    std::vector<char> buff;
  };

  bob_simple_data data;
  std::mutex m;

  bob_with_mutex( bob_with_mutex const& o ):
    data(o.data)
  {}
  bob_with_mutex& operator=( bob_with_mutex const& o )
  {
    data = o.data;
    return *this;
  }
};

类型bob混合了互斥锁和一些数据。数据存储在子结构中。 bob_with_mutex的副本不能默认,但它只是说“复制数据,而不是其他任何东西”。

bob的副本有自己的互斥锁。

答案 2 :(得分:0)

一种可能性是将互斥锁类型更改为指向互斥锁的指针。您可以复制指针,但仍然只有一个互斥锁副本。您可能想要使用智能指针。