【问题标题】:How to implement a move constructor for a diamond-shaped inheritance?如何为菱形继承实现移动构造函数?
【发布时间】:2013-07-05 17:06:59
【问题描述】:

我有一个菱形的类层次结构,其中没有默认构造函数,也没有复制构造函数。我有两个构造函数,一个是“移动”,另一个是对对象进行左值引用:

struct base {
    base(base&&) = default;
    base(member_type&& m): member_(std::move(m)) {}
    member_type member_;
};

struct virt_1: virtual base {
    virt_1(virt_1&& rhs): base(std::move(rhs)) {}
    virt_1(member_type&& m): base(std::move(m)) {}
};

struct virt_2: virtual base {
    virt_2(virt_2&& rhs): base(std::move(rhs)) {}
    virt_2(member_type&& m): base(std::move(m)) {}
};

struct concrete: virt_1, virt_2 {
    concrete(concrete&& rhs) // ???
};

除了不使用菱形层次结构外,是否可以为具体类实现移动构造函数?

谢谢!

【问题讨论】:

  • 对我来说听起来很容易出错。虚基的构造函数是从最派生类调用的,应该没有理由不能调用移动构造函数。但是任何进一步的推导,很有可能有人会忘记它,或者做错了。作为一般规则,虚拟基类不应包含数据,或至少具有除默认构造函数之外的任何构造函数,以避免此类问题。
  • @JamesKanze 我很清楚base 不应该包含数据,或者至少有一个默认的构造函数,但在我的上下文中不能这样。但是,如果有人在进一步推导时忘记调用虚拟基构造函数,编译器是否有可能会抱怨?
  • @piwi,不,没有机会,因为base没有默认构造函数,所以派生最多的类型必须显式构造它
  • @JamesKanze 我不得不承认“打破”钻石形状是可能的,但需要大量工作。正如人们指出的那样,对初始化程序的调用“无用”,这是我之前不知道/不理解的,我将不得不展望这种避免这种层次结构的解决方案:-/ 谢谢
  • @piwi 这只是一般规则。我确信有合法的例外。只要虚拟基类具有 no 默认构造函数,如果有人派生并且未在初始值设定项列表中提及,您应该会收到编译器错误。 (或者......将所有基类构造函数设为私有,并将您的派生类设为朋友,并且没有人可以从您的派生类派生。)

标签: c++ inheritance c++11


【解决方案1】:

要求编译器提供实现有什么问题?

concrete(concrete&&) = default;

我也将virt_1virt_2 移动构造函数定义为默认值。

如果你真的想写,你可以写出来:

concrete(concrete&& rhs)
: base(std::move(rhs)), virt_1(std::move(rhs)), virt_2(std::move(rhs))
{ }

或者如果你真的喜欢打字:

    concrete(concrete&& rhs)
    : base(static_cast<base&&>(rhs)),
      virt_1(static_cast<virt_1&&>(rhs)),
      virt_2(static_cast<virt_2&&>(rhs))
    { }

virt_1virt_2 基的初始化程序是无用的,因为它们只调用 base 构造函数并且因为它是一个虚拟基,所以当 concrete 调用它时它们不会这样做,但由于您选择的构造函数不能默认构造它们,并且需要用右值初始化它们,即使它们什么都不做。

【讨论】:

  • 问题:这会不会很危险?似乎它从同一个物体移动了多次......
  • 我认为我的问题在于具体类的两个构造函数;我将使用默认的移动构造函数进行展望
  • @AndyProwl,不,请参阅我添加的最后一段。 base 构造函数只会被调用一次(由最派生类型的构造函数调用),因此所有其他构造函数实际上不会对其右值参数做任何事情。只会有一次移动(请记住,调用std::move 实际上不会移动任何东西,只有当某物接受右值参数并选择从中移动时才会发生移动。)
  • 你说得对,最好让编译器替我处理;谢谢!
  • base(std::move(rhs)) 的调用在所有构造函数中都被跳过,除了最派生类的构造函数,参见 [class.base.init]/7: "A mem-initializer i> 其中 mem-initializer-id 表示在执行任何非派生类的构造函数时忽略虚拟基类。”
【解决方案2】:

当然。层次结构中某处具有virtual 基的任何构造函数都负责初始化该基。本质上,层次结构中virtual 基数之下的任何类都继承了初始化基数的权限。

在这种情况下,默认的移动构造函数应该做正确的事情。我建议还指定 concrete : private virtual base 以澄清正在发生的事情。

Here 是一个工作演示。

【讨论】:

  • 不妨也测试一下移动赋值运算符。
  • 该类不必直接从基类继承。但它负责调用任何虚拟基的构造函数,即使它不继承自它们。
  • @JamesKanze 哦,我认为使用间接基数的 mem-initializer 是语法错误。它不是。不过,如果我错了,请纠正我,但区别只是语法/风格。从基类继承也没有区别,没有virtual从基类继承是非法的。
  • 除非我犯了错误,否则我对以 b.member_ (ideone.com/3SDYH4) 结尾的值有疑问;我对您的示例所做的修改是否有错误?
  • @piwi 我“实现”了基本移动构造函数来打印一条消息,但实际上并没有让它初始化任何东西。只需让它分配一个值。
猜你喜欢
  • 1970-01-01
  • 2013-02-27
  • 1970-01-01
  • 1970-01-01
  • 2012-04-24
  • 2014-03-08
  • 1970-01-01
  • 2010-09-18
  • 1970-01-01
相关资源
最近更新 更多