静态模板化 constexpr 嵌套 class 成员
Static templated constexpr nested class member
我有以下示例 class Foo
嵌套 class Bar
一切都是 constexpr
:
class Foo
{
private:
template <typename T>
struct Bar
{
constexpr Bar(){}
constexpr int DoTheThing() const
{
return 1;
}
};
public:
constexpr static auto b = Bar<int>{};
constexpr Foo() {}
constexpr int DoTheThing() const
{
return b.DoTheThing();
}
};
我想测试调用 Foo::DoTheThing
returns 1:
int main()
{
constexpr Foo f;
static_assert(f.DoTheThing() == 1, "DoTheThing() should return 1");
}
GCC 和 Clang 都在这里抱怨,但 MSVC 没有
GCC 说:
error: constexpr Foo::Bar<T>::Bar() [with T = int]
used before its definition
constexpr static auto b = Bar<int>{};
和Clang:
error: constexpr variable b
must be initialized by a constant expression
constexpr static auto b = Bar<int>{};
我不知道标准是否不允许这样做,但我的猜测是某种程度上 b
是一个不完整的类型。
让事情变得更有趣的是,如果我删除 constexpr
,或者如果我将 Bar
的定义移到 Foo
之外,我可以让 GCC 和 Clang 正常运行。
以下哪个编译器是正确的?
请注意,此问题的灵感来自以下内容:
- Simple constexpr LookUpTable in C++14(我的问题是这个未回答问题的一部分)
- Nested struct breaks constexpr despite being identical to global ones(这似乎让我们对正在发生的事情有了一些了解)
来自 n4140
§ 9.2.2 [class.mem](强调我的)
A class is considered a completely-defined object type (3.9) (or
complete type) at the closing }
of the class-specifier. Within the
class member-specification, the class is regarded as complete within
function bodies, default arguments, using-declarations introducing
inheriting constructors (12.9), exception-specifications, and
brace-or-equal-initializers for non-static data members (including
such things in nested classes). Otherwise it is regarded as incomplete
within its own class member-specification.
Clang 和 GCC 是正确的。当您声明 static constexpr
成员时,class 不被认为是完整的,因此您无法构造它。这就是为什么将 Bar
的定义移出或删除 static constexpr
的原因(因为它 是 在定义非静态成员时被认为是完整的)
澄清一下,特别是考虑这个问题:Static constexpr member of an inner class
我上面引用的标准语基本上意味着除非另有说明一个class本身被认为是不完整的*。 static
、constexpr
或 static constexpr
初始化器不属于 其他指定的 部分,因此我们不能使用 在 class 中声明的任何内容,其中包括嵌套的 class 类型。
*意味着您不能在 class 声明中使用它或它的成员。最著名的例外是在成员函数中。
我有以下示例 class Foo
嵌套 class Bar
一切都是 constexpr
:
class Foo
{
private:
template <typename T>
struct Bar
{
constexpr Bar(){}
constexpr int DoTheThing() const
{
return 1;
}
};
public:
constexpr static auto b = Bar<int>{};
constexpr Foo() {}
constexpr int DoTheThing() const
{
return b.DoTheThing();
}
};
我想测试调用 Foo::DoTheThing
returns 1:
int main()
{
constexpr Foo f;
static_assert(f.DoTheThing() == 1, "DoTheThing() should return 1");
}
GCC 和 Clang 都在这里抱怨,但 MSVC 没有
GCC 说:
error:
constexpr Foo::Bar<T>::Bar() [with T = int]
used before its definitionconstexpr static auto b = Bar<int>{};
和Clang:
error: constexpr variable
b
must be initialized by a constant expressionconstexpr static auto b = Bar<int>{};
我不知道标准是否不允许这样做,但我的猜测是某种程度上 b
是一个不完整的类型。
让事情变得更有趣的是,如果我删除 constexpr
,或者如果我将 Bar
的定义移到 Foo
之外,我可以让 GCC 和 Clang 正常运行。
以下哪个编译器是正确的?
请注意,此问题的灵感来自以下内容:
- Simple constexpr LookUpTable in C++14(我的问题是这个未回答问题的一部分)
- Nested struct breaks constexpr despite being identical to global ones(这似乎让我们对正在发生的事情有了一些了解)
来自 n4140
§ 9.2.2 [class.mem](强调我的)
A class is considered a completely-defined object type (3.9) (or complete type) at the closing
}
of the class-specifier. Within the class member-specification, the class is regarded as complete within function bodies, default arguments, using-declarations introducing inheriting constructors (12.9), exception-specifications, and brace-or-equal-initializers for non-static data members (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification.
Clang 和 GCC 是正确的。当您声明 static constexpr
成员时,class 不被认为是完整的,因此您无法构造它。这就是为什么将 Bar
的定义移出或删除 static constexpr
的原因(因为它 是 在定义非静态成员时被认为是完整的)
澄清一下,特别是考虑这个问题:Static constexpr member of an inner class
我上面引用的标准语基本上意味着除非另有说明一个class本身被认为是不完整的*。 static
、constexpr
或 static constexpr
初始化器不属于 其他指定的 部分,因此我们不能使用 在 class 中声明的任何内容,其中包括嵌套的 class 类型。
*意味着您不能在 class 声明中使用它或它的成员。最著名的例外是在成员函数中。