92

以下代码非常简单,我希望它应该可以正常编译。

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.3 测试了这段代码。除了 g++ 4.7.2 在此代码 ( http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57770 ) 上存在段错误之外,其他经过测试的编译器给出的错误消息并不能解释太多。

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
};

这段代码真的不正确还是编译器错误?

4

2 回答 2

89

这段代码真的不正确还是编译器错误?

好吧,也不是。该标准有一个缺陷——它说在A解析. 这显然是循环的。考虑一下:B::iB::B()B::iA

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

这有一个矛盾:B::B()is 隐含noexcept当且当A()不抛出,并且A()当当且当B::B()is not noexcept时不抛出。在这个领域还有许多其他的循环和矛盾。

这由核心问题13601397跟踪。请特别注意核心问题 1397 中的此说明:

也许解决这个问题的最好方法是使非静态数据成员初始化器使用其类的默认构造函数的格式不正确。

这是我在 Clang 中为解决此问题而实施的规则的一个特例。Clang 的规则是,在解析该类的非静态数据成员初始化程序之前,不能使用该类的默认默认构造函数。因此 Clang 在这里发出诊断:

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

...因为 Clang 在解析默认初始值设定项之前先解析默认参数,并且此默认参数需要B已经解析过的默认初始值设定项(以便隐式定义B::B())。

于 2013-07-02T21:24:31.007 回答
0

也许这就是问题所在:

§12.1 5. 默认构造函数被默认且未定义为已删除的默认构造函数在使用 (3.2) 创建其类类型 (1.8) 的对象时或在其第一次声明后显式默认时被隐式定义

因此,第一次查找时会生成默认构造函数,但查找会失败,因为 A 未完全定义,因此无法找到 A 中的 B。

于 2013-07-02T19:16:58.983 回答