三阶规则与C++ 11一起成为五次规则?

Xeo*_*Xeo 325 c++ constructor rvalue-reference rule-of-three c++11

因此,在观看了关于右值引用的精彩演讲之后,我认为每个类都会受益于这样的"移动构造函数",template<class T> MyClass(T&& other) 编辑,当然还有"移动赋值运算符",template<class T> MyClass& operator=(T&& other)正如Philipp在他的回答中指出的,如果它已经动态分配成员,或通常存储指针.就像你应该有一个copy-ctor,赋值运算符和析构函数,如果之前提到的点适用.思考?

Phi*_*ipp 313

我要说三法则成为三,四,五的规则:

每个类应明确定义以下一组特殊成员函数:

  • 没有
  • 析构函数,复制构造函数,复制赋值运算符

此外,显式定义析构函数的每个类可以显式定义移动构造函数和/或移动赋值运算符.

通常,以下一组特殊成员函数是明智的:

  • 无(对于许多隐式生成的特殊成员函数正确且快速的简单类)
  • 析构函数,复制构造函数,复制赋值运算符(在这种情况下,类不可移动)
  • 析构函数,移动构造函数,移动赋值运算符(在这种情况下,该类将不可复制,对于底层资源不可复制的资源管理类很有用)
  • 析构函数,复制构造函数,复制赋值运算符,移动构造函数(因为复制省略,如果复制赋值运算符按值获取其参数,则没有开销)
  • 析构函数,复制构造函数,复制赋值运算符,移动构造函数,移动赋值运算符

请注意,不会为显式声明任何其他特殊成员函数的类生成移动构造函数和移动赋值运算符,不会为显式声明移动构造函数或移动的类生成复制构造函数和复制赋值运算符赋值运算符,并且具有显式声明的析构函数和隐式定义的复制构造函数或隐式定义的复制赋值运算符的类被视为已弃用.特别是,以下完全有效的C++ 03多态基类

class C {
  virtual ~C() { }   // allow subtype polymorphism
};
Run Code Online (Sandbox Code Playgroud)

应改写如下:

class C {
  C(const C&) = default;               // Copy constructor
  C(C&&) = default;                    // Move constructor
  C& operator=(const C&) = default;  // Copy assignment operator
  C& operator=(C&&) = default;       // Move assignment operator
  virtual ~C() { }                     // Destructor
};
Run Code Online (Sandbox Code Playgroud)

有点烦人,但可能比替代(自动生成所有特殊成员函数)更好.

与三巨头规则相反,未遵守规则可能会造成严重损害,未明确声明移动构造函数和移动赋值运算符通常很好,但在效率方面通常不是最理想的.如上所述,只有在没有显式声明的复制构造函数,复制赋值运算符或析构函数时,才会生成移动构造函数和移动赋值运算符.对于自动生成复制构造函数和复制赋值运算符,这与传统的C++ 03行为不对称,但更安全.因此,定义移动构造函数和移动赋值运算符的可能性非常有用,并创建了新的可能性(纯粹的可移动类),但遵循C++ 03三巨头规则的类仍然可以.

对于资源管理类,如果无法复制基础资源,则可以将复制构造函数和复制赋值运算符定义为已删除(计为定义).通常你仍然想要移动构造函数和移动赋值运算符.复制和移动赋值运算符通常使用swap,如在C++ 03中实现.如果你有一个移动构造函数和移动赋值运算符,则特殊化std::swap将变得不重要,因为泛型std::swap使用移动构造函数并移动赋值运算符(如果可用),并且应该足够快.

不用于资源管理的类(即,没有非空的析构函数)或子类型多态(即,没有虚拟析构函数)应该不声明五个特殊成员函数; 它们都将自动生成并且行为正确且快速.

  • 自从C++ 11通过以来,规则是否已经改变?我相信`struct C {virtual~C()= default; 现在允许`和最简洁的选项.来自n3242的禁止(" - 它不应该是虚拟的")在n3290中不再存在,而GCC允许它,而之前它没有. (10认同)
  • @BЈовић不,这不是一个错字.以下是一个很好的解释:http://stackoverflow.com/a/12306344/1174378 (3认同)
  • @Xeo:我相信如果该类不可复制,那么即使可以省略该副本,也无法通过值传递它的实例.在这种情况下,您应该使用rvalue引用声明一个真正的移动赋值运算符(赋值运算符的赋值运算符是一个§12.8/ 19的复制赋值运算符,如果该类不可复制,则不需要它).对于可复制和可移动的类,编译器应使用复制省略或调用移动构造函数. (2认同)

NoS*_*tAl 68

我无法相信没有人与此有关.

基本上,文章认为"零法则".我引用整篇文章是不合适的,但我认为这是重点:

具有自定义析构函数,复制/移动构造函数或复制/移动赋值运算符的类应专门处理所有权.其他类不应该有自定义析构函数,复制/移动构造函数或复制/移动赋值运算符.

这一点是恕我直言的重要:

常见的"包中所有"类包含在标准库中:std::unique_ptrstd::shared_ptr.通过使用自定义删除对象,两者都具有足够的灵活性来管理几乎任何类型的资源.

  • 见[here](http://chat.stackoverflow.com/transcript/10?m=5255227#5255227)和[here](http://chat.stackoverflow.com/transcript/10?m=5255286#5255286)对于我对这件事的看法.:) (4认同)

Mot*_*tti 19

我不这么认为,三个规则是一个经验法则,规定实现以下一个但不是全部的类可能是错误的.

  1. 复制构造函数
  2. 分配操作员
  3. 析构函数

但是,省略移动构造函数或移动赋值运算符并不意味着错误.它可能是错过了优化的机会(在大多数情况下),或者移动语义与此类无关,但这不是错误.

尽管在相关时定义移动构造函数可能是最佳实践,但这不是强制性的.在许多情况下,移动构造函数与类(例如std::complex)无关,并且在C++ 03中行为正确的所有类将继续在C++ 0x中正常运行,即使它们没有定义移动构造函数.


peo*_*oro 14

是的,我认为为这些类提供移动构造函数会很好,但请记住:

  • 这只是一种优化.

    仅实现一个或两个复制构造函数,赋值运算符或析构函数可能会导致错误,而没有移动构造函数可能会降低性能.

  • 无需修改即可始终应用移动构造函数.

    有些类总是分配它们的指针,因此这些类总是在析构函数中删除它们的指针.在这些情况下,您需要添加额外的检查,以确定其指针是已分配还是已被移走(现在为空).

  • 它不仅仅是一种优化,移动语义在完美转发中很重要,而某些类(`unique_ptr`)在没有移动语义的情况下也无法实现. (21认同)
  • `有些类总是分配它们的指针......在这种情况下,一个移动通常被实现为交换.同样简单快捷.(实际上更快,因为它将释放移动到右值的析构函数) (4认同)

And*_*alo 8

以下是自2011年1月24日以来当前状况及相关发展的简短更新.

根据C++ 11标准(见附件D的[depr.impldec]):

如果类具有用户声明的复制赋值运算符或用户声明的析构函数,则不推荐使用复制构造函数的隐式声明.如果类具有用户声明的复制构造函数或用户声明的析构函数,则不推荐使用复制赋值运算符的隐式声明.

它实际上被提议废弃被弃用的行为,使C++ 14成为真正的"五规则",而不是传统的"三规则".2013年,EWG投票反对该提案将在C++ 2014中实施.决定该提案的主要理由与对破坏现有代码的普遍关注有关.

最近,已提出再适应C++ 11的措辞,从而达到五非正式规则,即

如果用户提供了这些函数中的任何一个,则不能编译生成复制函数,移动函数或析构函数.

如果得到EWG的批准,C++ 17可能会采用"规则".