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

2024-04-06

下面的代码非常简单,我预计它应该可以正常编译。

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 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())
                       ^

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

or

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

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


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

嗯,都不是。该标准有一个缺陷——它说A在解析初始化器时被认为是完整的B::i, 然后B::B()(它使用初始化程序B::i) 可以在定义内使用A。这显然是循环的。考虑一下:

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

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

这是由核心问题跟踪的1360 http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1360 and 1397 http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1397。特别注意核心问题 1397 中的这条注释:

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

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

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐