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

时间:2019-05-10 21:25:28

标签: c++ language-lawyer constexpr default-constructor

我偶然发现了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%...

1 个答案:

答案 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;
      |     ^

live demo

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


[dcl.constexpr]/4

  

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是一个普通的类(与类模板相反), 并使其构造函数(仅)被声明 Bconstexpr必须具有A<int>构造函数, 情况并非如此。

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


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

constexpr

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

comment中所述, 初始化A a{}; B b{}; ,GCC(和标准)会很高兴。