显式默认的 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;
      |     ^

live demo

请注意,错误是在 B 的构造函数中引发的, 而不是 A, 其构造函数仅仅是“不能用作 constexpr 函数 因为 [the] 默认的默认构造函数 不初始化 int A<int>::v."


根据 [dcl.constexpr]/4:

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]);
  • [...]

这里,vint类型的,没有初始化。 因此,似乎 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.

因此,声明为 constexprA 的构造函数 实际上是有效的, 即使为 T = int!

实例化

问题是 B 的构造函数。 B 是普通的 class(与 class 模板相对), 并为其构造函数(仅)声明 constexprA<int> 必须有一个 constexpr 构造函数, 事实并非如此。

因此,应该像 GCC 一样拒绝此代码。


(请注意,两个编译器都拒绝对这种类型进行初始化, 例如:

A a{};
B b{};

以上代码被两个编译器拒绝。)

中所述, 初始化 A::v 并且 GCC(和标准)会很高兴。