clang vs gcc CRTP:constexpr 变量不能有非文字类型

clang vs gcc CRTP: constexpr variable cannot have non-literal type

我这里有一个 CRTP 模板 class:

template <typename S>
class Base
{
    public:
    constexpr static S NOT_SET{0};
};

struct Derived : public Base<Derived>
{
};

Clang (5.0.0) 不接受这个:

5 : <source>:5:24: error: constexpr variable cannot have non-literal type 'const Derived'
    constexpr static S NOT_SET{0};
                       ^
8 : <source>:8:25: note: in instantiation of template class 'Base<Derived>' requested here
struct Derived : public Base<Derived>
                        ^
5 : <source>:5:24: note: incomplete type 'const Derived' is not a literal type
    constexpr static S NOT_SET{0};
                       ^
8 : <source>:8:8: note: definition of 'Derived' is not complete until the closing '}'
struct Derived : public Base<Derived>
       ^
1 error generated.
Compiler exited with result code 1

但是 gcc(在 4.9.2 和 6.2 上测试)接受它就好了。

如何在 clang 中执行此操作?

当您尝试在 class 模板 Base 中使用

Derived 时,它不是一个完整的类型,因此您实际上不能那样使用它。那是因为类型必须是完整的才能声明该类型的变量。没有办法解决它。
总而言之,一个类型在结束时完成 }(以及与您的情况无关的其他异常,例如在其成员函数中)。
标准是这样说的(working draft):

A class is considered a completely-defined object type (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, noexcept-specifiers, and default member initializers (including such things in nested classes).
Otherwise it is regarded as incomplete within its own class member-specification.

因此 clang 是正确的,错误说的差不多。


如评论中所述,存在一种解决方法。只要派生类型是(让我说)constexpr constructible,你就可以在基础 class 中定义一个 constexpr 函数,returns 你是它的 未设置版本(不管它是什么意思)。