显式默认的constexpr ctor是否应允许非constexpr初始化

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%...

L. *_* F. 6

问题归结为:如果未使用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)

live demo

请注意,错误发生在的构造函数上B,而不是的A构造函数上,该构造函数仅“ constexpr由于默认默认构造函数未初始化而无法用作函数int A<int>::v”。


[dcl.constexpr] / 4

constexpr构造函数的定义应满足以下要求:

  • 该类不得具有任何虚拟基类;
  • 每个参数类型应为文字类型。

此外,其功能主体应为= delete,或应满足以下要求:

  • [...]
  • 每个非变量非静态数据成员和基类子对象都应初始化([class.base.init]);
  • [...]

在这里,v类型为int,并且未初始化。因此,似乎A 无法声明的构造函数constexpr

但是,[dcl.constructor] / 6说:

如果constexpr函数模板或类模板的成员函数的实例化模板专业化不能满足constexpr函数或constexpr构造函数的要求,则即使调用该函数,该专业化仍然是constexpr函数或constexpr构造函数。不能出现在常量表达式中。如果在将模板视为非模板函数或构造函数时,如果模板的特殊化都无法满足constexpr函数或constexpr构造函数的要求,则模板格式错误,无需诊断。

因此,构造A一个声明constexpr 实际上是有效的,实例化,即使T = int


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

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


(请注意,两个编译器都拒绝此类初始化,例如:

A a{};
B b{};
Run Code Online (Sandbox Code Playgroud)

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

评论中所述,initialize A::v和GCC(以及标准)将很高兴。