constexpr 结构成员初始化

constexpr struct member initialisation

此代码编译:

struct Info
{
    constexpr Info(bool val) : counted(false), value(unsigned(val)) {}
    constexpr Info(unsigned val) : counted(true), value(val) {}

    bool counted;
    unsigned value;
};

constexpr const auto data = std::array{
    Info{true}, Info{42u}
};

struct Foo
{
    constexpr static inline const auto data = std::array{
        Info{true}, Info{42u}
    };
};

此代码不:

struct Foo
{
    struct Info
    {
        constexpr Info(bool val) : counted(false), value(unsigned(val)) {}
        constexpr Info(unsigned val) : counted(true), value(val) {}

        bool counted;
        unsigned value;
    };

    constexpr static inline const auto data = std::array{
        Info{true}, Info{42u}
    };
};

报告的错误(在 MSVC、gcc 和 clang 中)表明他们认为 Info 构造函数未定义或不是 constexpr,例如。来自 clang:

prog.cc:21:5: note: undefined constructor 'Info' cannot be used in a constant expression
    Info{true}, Info{42u}
    ^

为什么?

(可能与有关,但Info在使用时应该是完整的;只有Foo还不完整。)

gcc-8的报错信息可以说更清楚了:

   constexpr Foo::Info::Info(bool)’ called in a constant expression before 
   its definition is complete

看来错误是根据[expr.const]§2:

产生的

An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (4.6), would evaluate one of the following expressions:

...

(2.3) — an invocation of an undefined constexpr function or an undefined constexpr constructor;

怎么会是undefined,调用的时候明明是定义之后?

问题是,成员函数定义被延迟到 最外层封闭 class 的右大括号(因为它们可以看到封闭 class 的成员es)。考虑这个 class 定义:

constexpr int one = 1;

struct Foo
{
    struct Bar
    {
        static constexpr int get_one() { return one; }
    };

    static constexpr int two = Bar::get_one() + 1;
    static constexpr int one = 42;
};

假设这应该有效,实施过程如何处理这个定义?

Bar::get_one里面的

one指的是Foo::one,而不是::one,所以一定是看到那个成员后才处理。它用在 two 的定义中,它是 constexpr,因此它必须在该成员的初始化程序之前处理。所以要让它起作用,总的顺序必须是 one,然后是 get_one,然后是 two

但是 C++ 实现不是这样工作的。他们不做任何复杂的依赖分析。它们按照可见的顺序处理声明和定义,[class.mem] §2 中列出了一些例外情况。

我似乎无法在标准中明确提及 constexpr 成员函数被视为未定义,直到 oitermost 封闭 class 完成,但这是唯一合乎逻辑的可能性。它不能以任何其他方式工作。