为什么std::copyable包含std::moveable



根据cppreference,std::copyable定义如下:

template <class T>
concept copyable =
std::copy_constructible<T> &&
std::movable<T> && // <-- !!
std::assignable_from<T&, T&> &&
std::assignable_from<T&, const T&> &&
std::assignable_from<T&, const T>;

我想知道为什么一个可复制的对象也应该是可移动的。只需考虑一个由多个函数访问的全局变量。虽然复制该变量是有意义的(例如,在调用另一个函数之前保存其状态(,但移动它没有意义,而且实际上非常糟糕,因为其他函数可能不知道该变量当前处于未指定的状态。那么,为什么std::copyable包含std::movable呢?

这来自两个事实。首先,即使您没有定义移动构造函数+移动赋值,如果您定义了复制函数,您仍然可以从r值引用构造/赋值对象。看看这个例子:

#include <utility>
struct foo {
foo() = default;
foo(const foo&) = default;
foo& operator=(const foo&) = default;
};
int main()
{
foo f;
foo b = std::move(f);
}

其次(也许更重要的是(,可复制类型总是可以(或者根据现在的标准必须是(以某种方式移动。若对象是可复制的,那个么移动的最坏情况就是复制内部数据。

注意,由于我声明了复制构造函数,编译器没有生成默认的移动构造函数。

虽然复制该变量是有意义的(例如,在调用另一个函数之前保存其状态(,但移动它毫无意义,实际上会非常糟糕,因为其他函数可能不知道该变量当前处于未指定状态。

对于移动的实际含义,有一个强有力的、未说明的假设,这可能是混乱的根源。考虑类型:

class Person {
std::string name;
public:
Person(std::string);
Person(Person const& rhs) : name(rhs.name) { }
Person& operator=(Person const& rhs) { name = rhs.name; return *this; }
};

移动Person有什么作用?Person型的右价可以与Person const&结合。。。那将是唯一的候选人。。。因此移动将调用复制构造函数。移动复制!这也不是一种罕见的情况-移动不具有的破坏性或比复制更有效,它只是可以

从广义上讲,有四类正常的类型:

  1. 移动和复制执行相同操作的类型(例如int(
  2. 移动可以是消耗资源的副本优化的类型(例如stringvector<int>(
  3. 可以移动但不能复制的类型(例如unique_ptr<int>(
  4. 既不能移动也不能复制的类型(例如mutex(

有很多类型属于第1组。OP中提到的变量也应该属于第1组。

值得注意的是,该列表中缺少一个可复制但不能移动的类型,因为从操作角度来看,这几乎没有意义。如果您可以复制类型,并且不希望移动时出现破坏性行为,只需使移动复制类型即可。

因此,您可以将这些组视为一种层次结构。(3( 在(4(上展开,(1(和(2(在(3(上展开——你无法从语法上真正区分(1(与(2(。因此,可复制包含可移动。

最新更新