静态模板化 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 正常运行。

以下哪个编译器是正确的?

请注意,此问题的灵感来自以下内容:

来自 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本身被认为是不完整的*staticconstexprstatic constexpr 初始化器不属于 其他指定的 部分,因此我们不能使用 在 class 中声明的任何内容,其中包括嵌套的 class 类型。

*意味着您不能在 class 声明中使用它或它的成员。最著名的例外是在成员函数中。