此代码编译:
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}
^
为什么?
(可能与this question相关,但Info
应该在使用时完整;只有Foo
仍然不完整。)
gcc-8的错误消息可以说更清楚:
constexpr Foo::Info::Info(bool)’ called in a constant expression before its definition is complete
似乎错误是根据[expr.const]§2产生的:
表达式
e
是核心常量表达式,除非根据抽象机器(4.6)的规则评估e
将评估以下表达式之一:...
(2.3) - 调用未定义的constexpr函数或未定义的constexpr构造函数;
为什么这个定义之后的电话明显是未定义的?
问题是,成员函数定义被延迟到最外层封闭类的右括号(因为它们可以看到封闭类的成员)。考虑这个类定义:
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;
};
假设这应该有效,那么实现如何处理这个定义呢?
one
里面的Bar::get_one
指的是Foo::one
,而不是::one
,因此必须在看到该成员之后进行处理。它用于two
的定义,它是constexpr,因此必须在该成员的初始化之前进行处理。所以为了这个工作,总体顺序必须是one
,然后是get_one
,然后是two
。
但是C ++实现不能这样工作。他们不做任何复杂的依赖性分析。它们按照被看到的顺序处理声明和定义,[class.mem]§2中列出了一些例外。
我似乎无法在标准中明确提到constexpr成员函数在最完整的封闭类完成之前被认为是未定义的,但这是唯一合乎逻辑的可能性。它不能以任何其他方式工作。