显式默认的 constexpr ctor 是否应该允许非 constexpr 初始化
Should an explicitly defaulted constexpr ctor allow non-constexpr initialization
我偶然发现了 GCC 和 Clang 之间关于显式默认的 constexpr 构造函数和一些继承的以下差异...
template <typename T>
struct A {
constexpr A() = default;
T v;
};
struct B : A<int> {
constexpr B() = default;
};
GCC 立即拒绝该代码,而 Clang 允许实例化这两种类型的非 constexpr 版本。我的猜测是 Clang 可能是正确的,但我不能 100% 确定...
问题归结为:
是 default-initializes 的 constexpr 构造函数
某些 non-static 内置类型有效的数据成员,
如果不使用?
tl;博士:
对于 non-template 构造函数,
不,让任何 non-static 数据成员未初始化是无效的。
对于模板构造函数,是的,
有一些(但不是全部,不需要诊断)是有效的
实例化模板特化
实例化的构造函数不满足要求
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
."
The definition of a constexpr constructor shall satisfy the following
requirements:
- the class shall not have any virtual base classes;
- each of the parameter types shall be a literal type.
In addition, either its function-body shall be = delete
, or it
shall satisfy the following requirements:
- [...]
- every non-variant non-static data member and base class subobject
shall be initialized ([class.base.init]);
- [...]
这里,v
是int
类型的,没有初始化。
因此,似乎 A
的构造函数
无法声明 constexpr
.
然而,[dcl.constructor]/6 说:
If the instantiated template specialization of a constexpr function
template or member function of a class template would fail to satisfy
the requirements for a constexpr function or constexpr constructor,
that specialization is still a constexpr function or constexpr
constructor, even though a call to such a function cannot appear in a
constant expression. If no specialization of the template would
satisfy the requirements for a constexpr function or constexpr
constructor when considered as a non-template function or constructor,
the template is ill-formed, no diagnostic required.
因此,声明为 constexpr
的 A
的构造函数
实际上是有效的,
即使为 T = int
!
实例化
问题是 B
的构造函数。
B
是普通的 class(与 class 模板相对),
并为其构造函数(仅)声明 constexpr
,
A<int>
必须有一个 constexpr
构造函数,
事实并非如此。
因此,应该像 GCC 一样拒绝此代码。
(请注意,两个编译器都拒绝对这种类型进行初始化,
例如:
A a{};
B b{};
以上代码被两个编译器拒绝。)
如 中所述,
初始化 A::v
并且 GCC(和标准)会很高兴。
我偶然发现了 GCC 和 Clang 之间关于显式默认的 constexpr 构造函数和一些继承的以下差异...
template <typename T>
struct A {
constexpr A() = default;
T v;
};
struct B : A<int> {
constexpr B() = default;
};
GCC 立即拒绝该代码,而 Clang 允许实例化这两种类型的非 constexpr 版本。我的猜测是 Clang 可能是正确的,但我不能 100% 确定...
问题归结为: 是 default-initializes 的 constexpr 构造函数 某些 non-static 内置类型有效的数据成员, 如果不使用?
tl;博士:
对于 non-template 构造函数, 不,让任何 non-static 数据成员未初始化是无效的。
对于模板构造函数,是的, 有一些(但不是全部,不需要诊断)是有效的 实例化模板特化 实例化的构造函数不满足要求 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
."
The definition of a constexpr constructor shall satisfy the following requirements:
- the class shall not have any virtual base classes;
- each of the parameter types shall be a literal type.
In addition, either its function-body shall be
= delete
, or it shall satisfy the following requirements:
- [...]
- every non-variant non-static data member and base class subobject shall be initialized ([class.base.init]);
- [...]
这里,v
是int
类型的,没有初始化。
因此,似乎 A
的构造函数
无法声明 constexpr
.
然而,[dcl.constructor]/6 说:
If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, that specialization is still a constexpr function or constexpr constructor, even though a call to such a function cannot appear in a constant expression. If no specialization of the template would satisfy the requirements for a constexpr function or constexpr constructor when considered as a non-template function or constructor, the template is ill-formed, no diagnostic required.
因此,声明为 constexpr
的 A
的构造函数
实际上是有效的,
即使为 T = int
!
问题是 B
的构造函数。
B
是普通的 class(与 class 模板相对),
并为其构造函数(仅)声明 constexpr
,
A<int>
必须有一个 constexpr
构造函数,
事实并非如此。
因此,应该像 GCC 一样拒绝此代码。
(请注意,两个编译器都拒绝对这种类型进行初始化, 例如:
A a{};
B b{};
以上代码被两个编译器拒绝。)
如 A::v
并且 GCC(和标准)会很高兴。