【问题标题】:Copy elision for direct base class initialization? [duplicate]复制省略直接基类初始化? [复制]
【发布时间】:2018-12-21 11:02:30
【问题描述】:

由于B 基类子对象在A 构造函数中的复制构造,以下代码无法同时使用Gcc 和Clang 编译:

struct B{
  B();
  B(const B&) =delete;
  };

struct A:B{
  A():B(B()){} //=> error: use of deleted function...
  };

不过根据[class.base.init]/7

mem-initializer 中的 expression-listbraced-init-list 用于初始化指定的子对象(或者,在委托构造函数的情况下,完整的类对象)根据[dcl.init]的初始化规则进行直接初始化。

因此,对于成员或直接基类,初始化规则是相同的。对于成员子对象,Gcc 和 Clang 不使用已删除的复制构造函数:

struct A2:B{
  B b;
  A2():b(B()){} //=> OK the result object of B() is b
  };

这不是 Clang 和 Gcc 的编译器错误吗? B的复制构造函数不应该在A()中省略吗?


有趣的是,即使 gcc 检查复制构造是否格式正确,它也会忽略此复制构造函数调用,请参阅 assembly here

【问题讨论】:

  • Nice Q...你是否对空白感到烦恼?
  • @YSC Whitespace 不再是我的首选编程语言了!
  • 我将“委托构造函数”解析为委托构造函数。如,委托给同一类的另一个构造函数,而不是基类的构造函数。所以我看不到引用的子句如何应用于基类。
  • @SamVarshavchik 你应该寻找suboject的定义!
  • @xskxzr 确实在回答我的问题。

标签: c++ language-lawyer c++17


【解决方案1】:

这确实很奇怪。首先,与是否应在此处调用复制构造函数无关,因为[class.base.init]/7 不区分初始化基类和初始化成员,因此两种情况下的行为应该相同。我在标准中找不到任何额外的措辞,它们会以某种方式在基的初始化与成员的初始化之间引入不对称。仅基于这一点,我想说我们可以得出结论,这里或多或少都存在编译器错误。 icc 和 MSVC 在初始化基类和初始化成员时似乎至少表现一致。但是,icc 和 MSVC 在代码是否应该被接受的问题上存在分歧。

如果我们再看[class.base.init]/7,相信可以找到是否应该调用复制构造函数的答案:

mem-initializer 中的表达式列表或花括号初始化列表用于初始化指定的子对象(或者,在委托构造函数的情况下,完整的类对象)根据初始化规则[dcl.init] 用于直接初始化。 […]

强调我的。我相信[dcl.init] 的相关位应该是[dcl.init]/17.6,我们在哪里找到:

  • 如果目标类型是(可能是 cv 限定的)类类型:

    • 如果初始化表达式是纯右值并且源类型的 cv 非限定版本与目标类是同一类,则使用初始化表达式初始化目标对象。 […]

    • 否则,如果初始化是直接初始化,或者如果是复制初始化,其中源类型的 cv 非限定版本与相同的类或派生类其中,目的地的类,考虑构造函数。 枚举适用的构造函数([over.match.ctor]),并通过重载决议([over.match])选择最佳构造函数。 […]

[…]

如果要应用 17.6.2,这意味着应该调用复制构造函数,这将使 MSVC 成为本示例中唯一行为正确的主要编译器。但是,我的解释是 17.6.1 一般适用并且 icc 是正确的,即您的代码应该编译。这意味着您在这里所拥有的甚至可能是 GCC 和 clang 中的两个错误(基础的初始化与成员的初始化行为不同 + mem-initializer 调用了 copy-ctor,尽管它不应该),还有一个在 MSVC 中……

【讨论】:

  • 这里有一个猜测:因为B的构造函数用于初始化A,所以不是“源类型”B和“目标”A,这会使"源类型与目标的类是同一类" false?
  • @Nelfeal 我不这么认为。要初始化的对象是A对象内的B基类子对象……
  • 第一段如果初始化表达式是prvalue并且源类型的cv非限定版本与目标类是同一类适用于此。初始化表达式:B() 是 B 类型的纯右值,目标类型是 B。
  • 我也这么认为。在这种情况下,我猜你在 GCC、clang 和 MSVC 中发现了一个错误……
  • @Oliv:但是 [class.base.init]/7 说“根据 [dcl.init] 的初始化规则进行直接初始化。”。因此,如果我们查看这条规则,那么我们不应该考虑第一个要点,因为它与直接初始化无关。
猜你喜欢
  • 2014-04-22
  • 2022-11-09
  • 1970-01-01
  • 1970-01-01
  • 2010-11-06
  • 1970-01-01
相关资源
最近更新 更多