问题 移动构造函数可以隐含吗?


考虑以下课程:

class A
{
public:
   std::string field_a;
   std::string field_b;
}

现在考虑以下复制结构:

A a1(a2);

复制结构将充分复制 A 尽管缺少显式的复制构造函数,因为复制构造函数为 std::string 将由编译器生成的隐式复制构造函数调用。

我想知道的是,移动建筑也是如此吗?

编辑:测试 这里 表明:

A a2(std::move(a1));

实际上会导致复制构造,除非特定的移动构造函数:

A( A && other ) : a(std::move(other.a)) {}

被定义为。

编辑编辑 我谴责了Stephan T Lavavej,并问他为什么VC 2012似乎没有遵循12.8关于隐式移动构造函数生成的草案。他很友善地解释道:

它更像是一个“尚未实现的功能”,而不是一个bug。 VC目前   实现我所称的右值引用v2.0,其中move   ctors / assign永远不会隐式生成,永远不会影响   隐式生成复制ctors / assign。 C ++ 11指定rvalue   引用v3.0,这是你正在看的规则。


1446
2017-11-12 13:42


起源

看看这个答案: stackoverflow.com/a/10402308/1741542 - Olaf Dietsche


答案:


是的,来自C ++ 11草案,12.8:

如果类X的定义没有显式声明移动构造函数,则将隐式声明一个   当且仅当如此,违约

  • X没有用户声明的复制构造函数,
  • X没有用户声明的复制赋值运算符,
  • X没有用户声明的移动赋值运算符,
  • X没有用户声明的析构函数,和
  • 移动构造函数不会被隐式定义为已删除。

后面的条件稍后详细说明:

隐式声明的复制/移动构造函数是其类的内联公共成员。如果X具有以下内容,则将类X的默认复制/移动构造函数定义为已删除(8.4.3):

  • 具有非平凡对应构造函数的变体成员,X是类似联合的类,
  • 类类型M(或其数组)的非静态数据成员,由于无法复制/移动   重载决策(13.3),应用于M的相应构造函数,导致模糊或a   从默认构造函数中删除或无法访问的函数,
  • 由于重载决策(13.3)而无法复制/移动的直接或虚拟基类B.   应用于B的相应构造函数,导致歧义或被删除的函数或   默认构造函数无法访问,
  • 具有被删除的析构函数的类型的任何直接或虚拟基类或非静态数据成员   或默认构造函数无法访问,
  • 对于复制构造函数,rvalue引用类型的非静态数据成员,或
  • 对于移动构造函数,一个非静态数据成员或直接或虚拟基类,其类型不具有移动构造函数,并且不易于复制。

简而言之,如果符合以下情况,将隐式声明移动构造函数:

  1. 该类没有用户声明的任何其他特殊成员函数。
  2. 移动构造函数可以通过移动其所有成员和基础来合理地实现。

你的班级显然符合这些条件。


7
2017-11-12 14:34



所以看看我追加的编辑,为什么我没有得到隐式移动结构? - Benj
Nitpick:从我有的草案(2011-02-28),这是“12.8”部分。 - Olaf Dietsche
ideone.com/1tBOpe 似乎表明,对于g ++,需要一个显式的移动构造函数。 - Benj
@Benj:也许是编译错误?或者代码可能与您复制的完全不同。我尝试了它,它适用于GCC 4.7.2。但是,在ideone中,与GCC 4.5相同的代码可以 不 工作。 - rodrigo
@OlafDietsche:我也是,这是一个错字!谢谢! - rodrigo


如果可以,并且如果没有用户定义的复制构造函数,编译器将合成移动构造函数。如果存在复制构造函数则没有合成移动构造函数的限制旨在避免破坏现有代码。当然,所有成员都需要移动。确切的规则涉及更多。


5
2017-11-12 14:01



除非专门删除自动生成的构造函数,否则总是不会有复制构造函数?换句话说,移动建设 std::string 除非定义了明确的移动构造函数,否则不会发生。 - Benj
@Benj:我想,我会在适当的地方进入“用户定义”。合成的拷贝构造函数不计算在内。 - Dietmar Kühl
那么为什么在这个程序中似乎需要一个显式的移动构造函数: ideone.com/1tBOpe (注意移动构造函数被注释掉的区别) - Benj
@Benj:这个节目应该展示什么?它不会显示字符串是否被移动:两个对象可以连接或可以断开连接。特别是,从字符串移动可以保持相当随机的状态。此外,在编译器上我尝试使用(gcc和clang)都产生相同的输出,不管是否定义了移动构造函数。您可以使用仪表式可移动类型观察移动。 - Dietmar Kühl
Dietmar std::string 实现我的编译器使用的叶子从字符串移动看起来像一个空字符串,因此可以通过查看它是否为空来查看它是否已被移动。 - Benj