使用非静态数据成员和嵌套类构造函数的类内初始化时出错

下面的代码是相当微不足道的,我希望它应该编译好。

struct A { struct B { int i = 0; }; B b; A(const B& _b = B()) : b(_b) {} }; 

我用g ++版本4.7.2,4.8.1,clang ++ 3.2和3.3testing了这个代码。 除了这个代码( http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57770 )上的g ++ 4.7.2段错误之外,其他testing编译器给出的错误信息并不能解释太多。

g ++ 4.8.1:

 test.cpp: In constructor 'constexpr A::B::B()': test.cpp:3:12: error: constructor required before non-static data member for 'A::B::i' has been parsed struct B ^ test.cpp: At global scope: test.cpp:11:23: note: synthesized method 'constexpr A::B::B()' first required here A(const B& _b = B()) ^ 

铿锵++ 3.2和3.3:

 test.cpp:11:21: error: defaulted default constructor of 'B' cannot be used by non-static data member initializer which appears before end of class definition A(const B& _b = B()) ^ 

使这个代码可编译是可能的,似乎应该没有什么区别。 有两个选项:

 struct B { int i = 0; B(){} // using B()=default; works only for clang++ }; 

要么

 struct B { int i; B() : i(0) {} // classic c++98 initialization }; 

这段代码真的不正确或编译器错了吗?

这段代码真的不正确,或者编译器错了吗?

那么,也没有。 该标准有一个缺陷 – 它表示A在parsingB::i的初始化程序时被认为是完整的,而B::B() (它使用B::i的初始化程序)可以在定义中使用A 这显然是循环的。 考虑一下:

 struct A { struct B { int i = (A(), 0); }; A() noexcept(!noexcept(B())); }; 

这有一个矛盾: B::B()是隐式的, noexcept A()不抛出,而A()不抛出iff B::B() 不是 noexcept 。 这个领域还有其他一些循环和矛盾。

这是由核心问题1360和1397跟踪。 特别注意核心问题1397中的这个注释:

也许解决这个问题的最好方法是让非静态数据成员初始化器使用它的类的默认构造函数来形成它。

这是我在Clang实施的解决这个问题的一个特例。 Clang的规则是,在类的非静态数据成员初始值设定项被parsing之前,不能使用默认的类的默认构造函数。 因此,Clang在这里发布一个诊断:

  A(const B& _b = B()) ^ 

…因为Clang在分析默认初始值之前parsing默认参数,而这个默认参数需要B的默认初始值已经被parsing(为了隐式定义B::B() )。

也许这是问题:

§12.15.默认构造函数是默认构造函数,当它被odr-use(3.2)创build一个类types的对象(1.8),或者在它的第一个声明后显式默认

因此,第一次查找时会生成默认的构造函数,但查找将失败,因为A没有完全定义,因此A中的B将不会被find。