将类定义传递给基类时是否完成了类定义?
我想写一个CRTP来包含派生类型的static constexpr
,因为这对于一个类是不可能的。此代码在GCC中编译良好,但是clang抱怨Derived
是不完整的类型。哪一个是对的?将类定义传递给基类时是否完成了类定义?
template<class T>
class Base {
public:
static constexpr T a = T(1), b = T(20);
};
class Derived : public Base<Derived> {
public:
int x;
constexpr Derived(int x) : x(x) {}
};
Derived
是在该Base<Derived>
被实例化([class.mem]/2)的点,这右在定义时发生不完全的。你使用了constexpr
,它需要根据[class.static.data]/3初始化器,并且当Base<Derived>
被实例化时,其静态数据成员([temp.inst]/3)的声明也是如此,其中包括初始化器。但是,初始化器试图创建一个不完整类型的对象,这是不合格的。
你可以声明你的成员作为const
代替:
template<class T>
class Base {
public:
static const T a;
};
template <typename T>
constexpr T Base<T>::a = T(1);
因为初始化现在是定义,这个初始化的实例可以推迟到例如Derived
已完成。 Demo与Clang。
请注意,Clang尚未将a
视为constexpr
,因为它未能急于实例化其定义。查看错误#24541。
但是你不能这样做'static_assert(Derived :: ax == 1,“”)'',这种类型会破坏'constexpr'的目的。 – user975989
@ user975989这似乎是一个叮当虫。 – Columbo
@ user975989找到DR并将其添加到答案中。 (有趣的是,使用的例子是非常重要的!) – Columbo
在Derived的构造函数中注意局部变量x和成员具有相同的名称 – Brahim
我只想指出,将基类中的派生对象保留为静态并不是最好的想法.. –
执行此操作的唯一原因这种方式是因为我不能把'Derived'类型的'static constexpr'放入'Derived'中。我希望能够这样做:'Derived :: a',而不是像'Derived :: constants :: a'这样的东西。 – user975989