我真的需要为const对象实现用户提供的构造函数吗?

我有这个代码:

class A { public: A() = default; private: int i = 1; }; int main() { const A a; return 0; } 

它在g ++(见ideone )上编译得很好,但在clang ++上失败,出现错误:

consttypes“const A”的对象的默认初始化需要用户提供的默认构造函数

我在LLVM bug跟踪器上报告了这个问题,并将其视为无效。

我认为试图说服铿锵的开发者绝对毫无意义。 另一方面,我看不出这种限制的原因。


任何人都可以build议,如果C ++ 11标准莫名其妙地暗示此代码是无效的? 或者我应该只是向g ++报告错误? 或者,也许在语言规则方面有足够的自由来处理这些代码?

N3797§8.5/ 7说:

如果程序调用const限定typesT的对象的默认初始化,则T应该是具有用户提供的默认构造函数的类types。

没有进一步的例子或解释。 我同意这看起来很奇怪。 此外,在C ++ 11中更新的规则比C ++ 03更加严格,当类types需要用户声明的构造函数时。 (你的构造函数是用户声明的。)

解决方法是使用{}请求值初始化,或者使用Dietmar的聪明的超类inline定义。

如果添加另一个没有初始化器的成员,GCC确实会提供一个诊断(相当不错的一个,指的是更新的C ++ 11要求)。

  private: int i = 1; int j; 
 unmem.cpp:11:11: error: uninitialized const 'a' [-fpermissive] const A a; ^ unmem.cpp:1:7: note: 'const class A' has no user-provided default constructor class A { ^ unmem.cpp:3:5: note: constructor is not user-provided because it is explicitly defaulted in the class body A() = default; ^ unmem.cpp:7:9: note: and the implicitly-defined constructor does not initialize 'int A::j' int j; 

GCC 源代表 DR 253 , 为什么必须初始化空或完全初始化的const对象? 这是标准中的一个未解决的问题,最近在2011年8月更新(后C ++ 11),注意:

如果隐式默认构造函数初始化所有子对象,则不需要初始化器。

因此,虽然Clang符合C ++ 11(并且将遵循C ++ 14的原样),但GCC正在实施标准化委员会的最新思想。

提交了一个GCC的错误 。 我预测,当(如果)错误得到修复时,您将需要-pedantic来进行诊断。

请注意,您可以将您的类轻松转换为具有用户定义的默认构造函数的类:

 class A { public: A(); private: int i = 1; }; inline A::A() = default; 

根据8.4.2 [dcl.fct.def.default]第4段:

…一个特殊的成员函数是用户提供的,如果它是用户声明的,并且没有明确地默认或删除它的第一个声明。 …

这暗示了一个没有明确默认的函数不是用户提供的。 结合8.5 [dcl.init]的第6段

…如果一个程序调用一个const限定typesT的对象的默认初始化,T应该是一个具有用户提供的默认构造函数的类types。

看来很清楚你不能使用默认的构造函数来默认它的第一个声明来初始化一个const对象。 但是,如果不是上述代码中的第一个声明,则可以使用默认定义。

编辑:以下是基于过时的信息。 我刚刚通过N3797,这是我发现:

§8.5 / 7 [dcl.init]
如果程序调用const限定typesT的对象的默认初始化,则T应该是具有用户提供的默认构造函数的类types

请注意下面的链接中的标准报价说用户声明


下面的程序在g ++中编译,但不是铿锵的++:

 struct A {}; void f() { A const a; } 

这可能与这个“固定”的错误报告有关。 一旦它包含数据成员,g ++就不能编译它,除非它们被初始化了。 注意, int member = 1将不再使A成为POD。 相比之下,铿锵声++则拒绝所有排列(空的类和数据成员被初始化或不被初始化)。下面的段落解释标准的意义:

§8.5 / 9 [dcl.init]说:

如果没有为对象指定初始化程序,并且对象是(可能是cv-qualified)非POD类types(或其数组),则该对象应该被默认初始化; 如果对象是const限定types的,则底层类types应该有一个用户声明的默认构造函数。 否则,如果没有为对象指定初始值设定项,则该对象及其子对象(如果有)具有不确定的初始值; 如果对象或其任何子对象是const限定types的,则该程序是不合格的。

请参阅为什么C ++需要用户提供的默认构造函数来默认构build一个const对象? 。 if the object is of const-qualified POD type, and there is no initializer specified (because POD are not default initialized).假定程序是不合格的if the object is of const-qualified POD type, and there is no initializer specified (because POD are not default initialized). 请注意g ++的行为如下:

 struct A {int a;}; struct B {int a = 1;}; int main() { A a; B b; const A c; // A is POD, error const B d; // B is not POD, contains data member initializer, no error }