C++ 直接基类初始化的复制省略?

C++ 直接基类初始化的复制省略?,c++,language-lawyer,c++17,C++,Language Lawyer,C++17,由于A构造函数中的B基类子对象的复制构造,以下代码无法使用Gcc和Clang进行编译: struct B{ B(); B(const B&) =delete; }; struct A:B{ A():B(B()){} //=> error: use of deleted function... }; struct A2:B{ B b; A2():b(B()){} //=> OK the result object of B() is b };

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

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

struct A:B{
  A():B(B()){} //=> error: use of deleted function...
  };
struct A2:B{
  B b;
  A2():b(B()){} //=> OK the result object of B() is b
  };
然而,根据:

mem初始值设定项中的表达式列表或带括号的init列表用于根据[dcl.init]的初始化规则直接初始化指定的子对象(或者,如果是委托构造函数,则是完整的类对象)

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

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

struct A:B{
  A():B(B()){} //=> error: use of deleted function...
  };
struct A2:B{
  B b;
  A2():b(B()){} //=> OK the result object of B() is b
  };
这不是Clang和Gcc的编译器错误吗?
A()
中不应该省略
B
的复制构造函数吗



有趣的是,即使gcc检查副本构造是否格式正确,它也会省略这个副本构造函数调用,请参见这确实非常奇怪。首先,不管是否应该在这里调用复制构造函数,因为不区分初始化基和初始化成员,所以在这两种情况下行为应该相同。我在标准中找不到任何额外的措辞会以某种方式在基础初始化和成员初始化之间引入不对称。仅基于此,我想说,我们可以得出这样或那样的结论,这里有一个编译器错误。icc和MSVC似乎至少在初始化基本成员和初始化成员时表现一致。然而,icc和MSVC对该准则是否应被接受存在分歧

我相信,如果我们再看一遍,就可以找到是否应该调用复制构造函数的答案:

mem初始值设定项中的表达式列表或括号内的init列表用于根据[dcl.init]的初始化规则初始化指定的子对象(或者,如果是委托构造函数,则是完整的类对象),以便直接初始化。[……]

我的。我认为相关的部分应该是,我们发现:

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

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

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

[……]


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

Nice Q。。。你对空白感到烦恼吗?@YSC不再是我喜欢的编程语言了!我将“委托构造函数”解析为一个委托构造函数。与中一样,委托给同一类的另一个构造函数,而不是基类的构造函数。所以我看不出quoted子句是如何应用于基类的。@SamVarshavchik您应该查找subject的定义@xskxzr确实回答了我的问题。这里有一个猜测:既然
B
的构造函数被用来初始化
a
,那么“源类型”
B
和“目的地”
a
“源类型与目的地的类是同一类”不是假的?@Nelfeal我不这么认为。要初始化的对象是
A
对象中的
B
基类子对象…如果初始值设定项表达式是prvalue,并且源类型的cv非限定版本与目标类在此处应用的类相同,则第一段。初始值设定项表达式:
B()
是类型B的prvalue,目标类型是B。我也这么认为。在本例中,我猜您在GCC、clang和MSVC中发现了一个bug…@Oliv:但是[class.base.init]/7说“根据[dcl.init]的初始化规则进行直接初始化。”。因此,如果我们看看这个规则,那么我们不应该考虑第一个要点,因为它不是关于直接初始化。