我偶然发现了GCC和Clang之间的以下区别,这些区别涉及显式默认的constexpr ctor和一些继承...
template <typename T>
struct A {
constexpr A() = default;
T v;
};
struct B : A<int> {
constexpr B() = default;
};
GCC立即拒绝代码,而Clang允许实例化两种类型的非constexpr版本。我的猜测是Clang可能是正确的,但我不确定100%...
答案 0 :(得分:6)
问题归结为: 是默认初始化的constexpr构造函数 某些内置类型的非静态数据成员有效, 如果不使用?
tl;博士:
对于非模板构造函数, 不,将所有非静态数据成员保留为未初始化是无效的。
对于模板构造函数,是的, 拥有一些(但不是全部,不需要诊断)是有效的 实例化模板专业化 实例化的构造函数不满足要求的情况 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;
| ^
请注意,错误是在B
的构造函数上引发的,
而不是A
,
其构造函数只是“不能用作constexpr
函数
因为[the]默认的默认构造函数
不会初始化int A<int>::v
。”
constexpr构造函数的定义应满足以下条件 要求:
- 该类不得包含任何虚拟基类;
- 每种参数类型应为文字类型。
此外,其 function-body 应该是
= delete
,或者 应满足以下要求:
- [...]
- 每个不变的非静态数据成员和基类子对象 应该初始化([class.base.init]);
- [...]
此处,v
的类型为int
,未初始化。
因此,看来A
的构造函数
无法声明为constexpr
。
但是,[dcl.constructor]/6说:
如果constexpr函数的实例化模板特化 模板或类模板的成员函数将无法满足 constexpr函数或constexpr构造函数的要求, 该专业化仍然是constexpr函数或constexpr 构造函数,即使对该函数的调用不能出现在 常数表达式。如果没有专门的模板会 满足constexpr函数或constexpr的要求 构造函数,当被视为非模板函数或构造函数时, 模板格式错误,无需诊断。
因此,声明为A
的{{1}}的构造函数
实际上是有效的
即使为constexpr
实例化了!
问题是T = int
的构造函数。
B
是一个普通的类(与类模板相反),
并使其构造函数(仅)被声明 B
,
constexpr
必须具有A<int>
构造函数,
情况并非如此。
因此,此代码应被拒绝,就像GCC一样。
(请注意,两个编译器都拒绝此类初始化, 例如:
constexpr
以上代码都被两个编译器拒绝。
如comment中所述,
初始化A a{};
B b{};
,GCC(和标准)会很高兴。