【问题标题】:Default move constructor in a sub class子类中的默认移动构造函数
【发布时间】:2014-09-04 11:47:51
【问题描述】:

在C++11中,如果基类已经定义了自己的移动(复制)构造函数(赋值运算符),那么它的子类是否需要在调用基类的地方定义自己的移动(复制)构造函数(赋值运算符)?相应的构造函数/操作符是否被显式调用?

每次都明确定义构造函数、析构函数、移动/复制构造函数(赋值运算符)是不是一个好主意?

struct Base {
    Base() {}
    Base(Base&& o);
};

struct Sub : public Base {
    Sub(Sub&& o) ;  // Need I do it explicitly ? If not,what the compiler will do for me
};

【问题讨论】:

  • 如果您没有任何移动构造函数,编译器将生成一个默认的移动构造函数,但并非总是如此(参见例如this reference for a list of when it's not created)。但是,您不能依赖默认的移动(或任何其他)构造函数来“做正确的事情”,尤其是不能依赖基类移动构造函数,因为它不知道任何子类。另请阅读the rule of three (or five since C++11)
  • @Puppy 我会说三规则和零规则相互补充,而不是排除或取代另一个。仅仅因为您可以对某些课程遵循零规则,并不意味着您可以(或应该)对所有课程都这样做。
  • 哦,参考 OP(和其他好奇)here's the rule of zero
  • 从实际的角度来看,截至目前(2014 年),许多 C++11 模式下的编译器(尤其是 MSVC)无法正确生成默认移动构造函数,因此最好始终编写自己的.
  • @c.r.许多?我知道有一个(MSVC)假装是 C++11,但缺少它。

标签: c++ c++11 move-semantics


【解决方案1】:

如果你没有在基类中指定一个移动构造函数,编译器会生成一个默认的移动构造函数(some cases 除外,例如有一个基类删除了移动构造函数),但无论如何你都应该显式调用基类”,如果有的话:

Sub(Sub&& o) : Base(std::move(o))

【讨论】:

  • 完美的转发效果如何? Sub&& 是一个右值引用,所以它只会作为右值转发。如果您使用通用引用和std::forward,那将是完美的转发。
  • @0x499602D2 没有完美转发 OP 的代码会产生错误,因为复制构造函数在 Base 中被隐式删除,我是否遗漏了什么?
  • 抱歉,我明白了你的意思:那里只能传递右值。我同意,这与术语意义上的完美转发无关。一个 std::move 在这里就足够了
  • 是的,而且还有对基类引用的隐式覆盖,因此std::move() 中不需要显式模板参数。
【解决方案2】:

根据标准(N379712.8/9 复制和移动类对象[class.copy]:

如果class X 的定义没有明确声明移动构造函数,当且仅当

— X 没有用户声明的复制构造函数,

——X 没有用户声明的复制赋值运算符,

——X 没有用户声明的移动赋值运算符,并且

— X 没有用户声明的析构函数。

因此,如果您的class 满足上述要求,那么将为您隐式声明一个默认的移动构造函数。

如前所述,基类不知道任何子类。因此,是否在一个基类中声明移动构造函数对在其子类中隐式生成移动构造函数没有影响。

就你是否应该显式声明一个类的构造函数/析构函数等而言,这很好article

【讨论】:

    【解决方案3】:

    不,你没有。我会像默认/复制构造函数一样自动生成。


    来自this page

    隐式声明的移动构造函数

    如果没有为类类型(结构、类或联合)提供用户定义的移动构造函数,并且以下所有情况都为真:

    there are no user-declared copy constructors
    there are no user-declared copy assignment operators
    there are no user-declared move assignment operators
    there are no user-declared destructors
    (until C++14) the implicitly-declared move constructor is not defined as deleted due to conditions detailed in the next section 
    

    然后编译器将使用签名 T::T(T&&) 将移动构造函数声明为其类的内联公共成员。

    一个类可以有多个移动构造函数,例如T::T(const T&&) 和 T::T(T&&)。如果存在一些用户定义的移动构造函数,用户仍然可以强制生成带有关键字 default 的隐式声明的移动构造函数。

    您的struct Sub 没有用户声明的复制构造函数、复制赋值运算符、移动赋值运算符或析构函数。

    还有,

    简单的移动构造函数

    如果满足以下所有条件,则类 T 的移动构造函数是微不足道的:

    It is not user-provided (meaning, it is implicitly-defined or defaulted), and if it is defaulted, its signature is the same as implicitly-defined
    T has no virtual member functions
    T has no virtual base classes
    The move constructor selected for every direct base of T is trivial
    The move constructor selected for every non-static class type (or array of class type) member of T is trivial 
    
    T has no non-static data members of volatile-qualified type 
    

    (C++14 起)

    普通移动构造函数是执行与普通复制构造函数相同的操作的构造函数,即如同通过 std::memmove 制作对象表示的副本。所有与 C 语言兼容的数据类型(POD 类型)都可以轻松移动。

    隐式定义的移动构造函数

    如果隐式声明的移动构造函数既不被删除也不平凡,它是由编译器定义的(即生成并编译一个函数体)。对于联合类型,隐式定义的移动构造函数复制对象表示(如 std::memmove)。 对于非联合类类型(类和结构),移动构造函数使用带有 xvalue 参数的直接初始化,按照初始化顺序对对象的基类和非静态成员执行完整的成员移动。

    Base 的移动构造函数并不简单(它是用户定义的)。因此,Sub 的隐式定义的移动构造函数将作为“移动构造函数执行对象的基类和非静态成员的完整成员移动,按照它们的初始化顺序,使用直接初始化带有 xvalue 参数。”

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-02
      • 2018-06-13
      • 2012-06-15
      • 2013-03-16
      • 1970-01-01
      相关资源
      最近更新 更多