三规则与C ++ 11成为五大规则?

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

我认为三项法则就是三,四,五规则:

每个类都应该明确定义下列一组特殊成员函数中的一个:

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

另外,每个明确定义析构函数的类都可以明确定义一个移动构造函数和/或一个移动赋值运算符。

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

  • 无(对于隐式生成的特殊成员函数正确和快速的许多简单类)
  • 析构函数,复制构造函数,复制赋值运算符(在这种情况下,类将不能移动)
  • 析构函数,移动构造函数,移动赋值运算符(在这种情况下,类将不可复制,对于底层资源不可复制的资源pipe理类非常有用)
  • 析构函数,复制构造函数,复制赋值运算符,移动构造函数(由于复制省略,如果复制赋值运算符通过值获取其参数,则不会有开销)
  • 析构函数,复制构造函数,复制赋值运算符,移动构造函数,移动赋值运算符

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

 class C { virtual ~C() { } // allow subtype polymorphism }; 

应改写如下:

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

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

与“三大规则”相比,如果不遵守规则会造成严重的损害,那么不明确地声明移动构造函数和移动赋值运算符通常很好,但在效率方面往往不是最理想的。 如上所述,只有在没有显式声明的复制构造函数,复制赋值运算符或析构函数的情况下,才会生成移动构造函数和移动赋值运算符。 这与传统C ++ 03行为在复制构造函数和复制赋值运算符的自动生成方面不是对称的,而是更安全。 所以定义移动构造函数和移动赋值操作符的可能性是非常有用的,并且创build了新的可能性(纯可移动类),但是遵循三巨头C ++ 03规则的类仍然可以。

对于资源pipe理类,如果无法复制底层资源,则可以将复制构造函数和复制赋值运算符定义为已删除(将其视为定义)。 通常你还是要移动构造函数和移动赋值运算符。 复制和移动赋值操作符通常使用swap来实现,就像在C ++ 03中一样。 如果你有一个移动构造函数和移动赋值运算符,专门化std::swap将变得不重要,因为通用std::swap使用移动构造函数和移动赋值运算符(如果可用),并且应该足够快。

不是为了资源pipe理(即没有非空的析构函数)或子types多态(即没有虚拟析构函数)的类应该声明五个特殊成员函数中的任何一个; 他们都将自动生成,并performance正确和快速。

我不敢相信没有人联系到这个 。

基本上文章主张“零规则”。 引用整篇文章是不恰当的,但我相信这是主要的观点:

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

另外这个位是恕我直言重要的:

标准库中包含常用的“所有权包”类: std::unique_ptrstd::shared_ptr 。 通过使用定制的删除对象,两者都已经变得足够灵活,几乎可以pipe理任何types的资源。

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

  1. 复制构造函数
  2. 赋值运算符
  3. 析构函数

然而,不考虑移动构造函数或移动赋值操作符并不意味着一个错误。 这可能是一个错过优化(大多数情况下)的机会,或者移动语义与这个类无关,但这不是一个错误。

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

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

  • 这只是一个优化。

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

  • 移动构造函数不能总是在没有修改的情况下应用。

    有些类总是分配指针,因此这些类总是删除析构函数中的指针。 在这些情况下,您需要添加额外的检查来说明他们的指针是分配的还是已经移走的(现在是空的)。

以下是自1月24日以来的最新状况和相关进展情况。

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

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

实际上已经提出过时的弃用的行为给C ++ 14一个真正的“五规则”,而不是传统的“三规则”。 EWG在2013年投票反对这个build议在C ++ 2014中实施。 提案决定的主要依据是关于打破现有代码的普遍担忧。

最近有人再次提出要适应C ++ 11的措辞,以达到非正式的五项规则,即

如果这些函数中的任何一个是用户提供的,则不会有编译器生成的复制函数,移动函数或析构函数。

如果得到EWG的批准,C ++ 17可能会采用“规则”。

基本上就是这样:如果你不宣布任何移动操作,你应该遵守三的规则。 如果你宣布一个移动操作,那么“违反”三条规则就没有什么坏处,因为编译器生成操作的生成已经变得非常严格。 即使你没有声明移动操作并违反三条规则,一个C ++ 0x编译器也会给你一个警告,以防一个特殊的函数被用户声明,而其他的特殊函数已经被自动生成,现在不推荐使用“C ++ 03兼容性规则”。

我认为可以肯定的是,这条规则变得不那么重要了。 C ++ 03中的真正问题在于,实现不同的复制语义要求用户声明所有相关的特殊函数,以便它们都不是编译器生成的(否则会做错误的事情)。 但是C ++ 0x改变了关于特殊成员函数生成的规则。 如果用户声明这些函数中的一个来改变复制语义,它将阻止编译器自动生成剩余的特殊函数。 这很好,因为缺less声明会将运行时错误转换为编译错误(至less是警告)。 作为一个C ++ 03兼容性度量,一些操作仍然生成,但是这一代被认为是不赞成的,并且应该至less在C ++ 0x模式下产生一个警告。

由于编译器生成的特殊函数和C ++ 03兼容性的限制性规则,三条规则仍然是三条规则。

以下是一些应用程序可以使用最新的C ++ 0x规则的例子:

 template<class T> class unique_ptr { T* ptr; public: explicit unique_ptr(T* p=0) : ptr(p) {} ~unique_ptr(); unique_ptr(unique_ptr&&); unique_ptr& operator=(unique_ptr&&); }; 

在上面的例子中,不需要声明任何其他特殊function被删除。 他们根本就不会因为限制规则而产生。 用户声明的移动操作的存在会禁止编译器生成的复制操作。 但在这样的情况下:

 template<class T> class scoped_ptr { T* ptr; public: explicit scoped_ptr(T* p=0) : ptr(p) {} ~scoped_ptr(); }; 

一个C ++ 0x编译器现在预计会产生一个警告,说明编译器生成的复制操作可能会做错误的事情。 在这里,三件事情的规则,应该受到尊重。 在这种情况下的警告是完全合适的,并给予用户处理错误的机会。 我们可以通过删除的function摆脱这个问题:

 template<class T> class scoped_ptr { T* ptr; public: explicit scoped_ptr(T* p=0) : ptr(p) {} ~scoped_ptr(); scoped_ptr(scoped_ptr const&) = delete; scoped_ptr& operator=(scoped_ptr const&) = delete; }; 

所以,三个规则仍然适用于这里,只是因为C ++ 03的兼容性。

不能破坏3的规则现在成为4(或者5)的规则,不破坏现在的所有强制规则3的代码,不实施任何forms的移动语义。

3的规则意味着如果你实现一个你必须实现所有3。

也不知道会有任何自动生成的移动。 “3的规则”的目的是因为它们自动存在,如果你实现一个,很可能其他两个的默认实现是错误的。

在一般情况下,是的,三人的规则刚刚成为五人之一,join了移动赋值运算符和移动构造函数。但并不是所有的类都是可复制和移动的,有的只是可移动的,有的只是可以复制的。