Vin*_*nci 4 c++ default-constructor language-lawyer constexpr
我只是偶然发现了GCC和Clang在显式默认constexpr ctor和某些继承方面的以下区别...
template <typename T>
struct A {
constexpr A() = default;
T v;
};
struct B : A<int> {
constexpr B() = default;
};
Run Code Online (Sandbox Code Playgroud)
GCC立即拒绝该代码,而Clang允许实例化这两种类型的非constexpr版本。我的猜测是Clang可能是正确的,但我不确定100%...
问题归结为:如果未使用constexpr构造函数,该constexpr构造函数默认初始化一些内置类型的非静态数据成员,则该构造函数有效吗?
tl; dr:
对于非模板构造函数,否,将所有非静态数据成员保留为未初始化是无效的。
对于模板构造函数,是的,具有一些(但不是全部,不需要诊断)实例化模板特化是有效的,对此实例化的构造函数不满足constexpr构造函数的要求。
在这种情况下,GCC是正确的,而Clang是错误的。
GCC给出以下错误消息,该消息非常有用:
prog.cc:8:13: error: explicitly defaulted function 'constexpr B::B()' cannot be declared as 'constexpr' because the implicit declaration is not 'constexpr':
8 | constexpr B() = default;
| ^
prog.cc:3:13: note: defaulted constructor calls non-'constexpr' 'A<T>::A() [with T = int]'
3 | constexpr A() = default;
| ^
prog.cc:3:13: note: 'A<T>::A() [with T = int]' is not usable as a 'constexpr' function because:
prog.cc:4:5: note: defaulted default constructor does not initialize 'int A<int>::v'
4 | T v;
| ^
Run Code Online (Sandbox Code Playgroud)
请注意,错误发生在的构造函数上B,而不是的A构造函数上,该构造函数仅“ constexpr由于默认默认构造函数未初始化而无法用作函数int A<int>::v”。
constexpr构造函数的定义应满足以下要求:
- 该类不得具有任何虚拟基类;
- 每个参数类型应为文字类型。
此外,其功能主体应为
= delete,或应满足以下要求:
- [...]
- 每个非变量非静态数据成员和基类子对象都应初始化([class.base.init]);
- [...]
在这里,v类型为int,并且未初始化。因此,似乎A
无法声明的构造函数constexpr。
如果constexpr函数模板或类模板的成员函数的实例化模板专业化不能满足constexpr函数或constexpr构造函数的要求,则即使调用该函数,该专业化仍然是constexpr函数或constexpr构造函数。不能出现在常量表达式中。如果在将模板视为非模板函数或构造函数时,如果模板的特殊化都无法满足constexpr函数或constexpr构造函数的要求,则模板格式错误,无需诊断。
因此,构造A一个声明constexpr
实际上是有效的,实例化,即使T = int!
问题是的构造函数B。
B是一个普通类(与类模板相对),并且要(仅)声明 其构造函数constexpr,
A<int>必须具有constexpr构造函数,事实并非如此。
因此,应该像GCC一样拒绝此代码。
(请注意,两个编译器都拒绝此类初始化,例如:
A a{};
B b{};
Run Code Online (Sandbox Code Playgroud)
上面的代码被两个编译器拒绝。)
如评论中所述,initialize A::v和GCC(以及标准)将很高兴。