是一个可用作constexpr变量的空类,没有初始值设定项或显式默认构造函数

Is an empty class usable as a constexpr variable without an initializer or explicit default constructor?

本文关键字:默认 构造函数 一个 变量 constexpr      更新时间:2023-10-16

给定以下代码:

struct f {
};
int main(){
    constexpr f f1 ;
    //const f f1  ; // This also has the same issue
    //constexpr f f1 = {} ; //This works
}

clang和gcc在它是否有效的问题上存在分歧,clang提供了以下诊断(实时查看):

error: default initialization of an object of const type 'const f' without a user-provided default constructor
constexpr f f1 ;
            ^
              {}

据我所知,f是一个文字类型,它是由隐式默认构造函数初始化的,这应该允许它被声明为constexpr。这里谁是正确的?

注意,如果我显式添加constexpr默认构造函数:,clang确实接受f1的声明

constexpr f() {} ;

答案是否改变为f不是一个聚合?

如果我们从草案C++14标准部分7.1.5[dcl.constexpr]开始,我们可以发现constexpr对象声明的要求是:

对象声明中使用的constexpr说明符将对象声明为const。此类对象应具有文字类型,并且应进行初始化。如果它是由构造函数调用初始化的,那么该调用应该是一个常量表达式(5.19)

那么f是文字类型吗?

3.9[基本类型]说:

如果类型是:,则该类型为文字类型

并涵盖以下项目符号中的类别:

  • 具有以下所有属性的类类型(第9条)

    • 它有一个琐碎的析构函数
    • 它是聚合类型(8.5.1),或者至少有一个constexpr构造函数或构造函数模板不是复制或移动构造函数,以及
    • 它的所有非静态数据成员和基类都是非易失性文本类型

所以我们在第一颗和第三颗子弹上都还好。为了覆盖第二个项目符号,我们可以注意到f聚合,但如果我们稍微修改示例,例如f看起来像这样:

struct f {
   private:
      int x = 0 ;
} ;

这在C++11或C++14中都不是一个集合,但问题仍然存在。然后我们需要证明它有一个constexpr构造函数。f有constexpr构造函数吗?

据我所知,12.1[class.ctor]部分表示同意:

[…]如果用户编写的默认构造函数满足constexpr构造函数的要求(7.1.5),隐式定义的默认构造函数是constexpr。[…]

但不幸的是,8.5节要求我们有一个用户提供的构造函数,其中写道:

如果程序调用常量限定类型T的对象的默认初始化,则T应为类类型具有用户提供的默认构造函数。

因此,clang在这里看起来是正确的,如果我们查看以下clang错误报告:"错误:const类型'constZ'的对象的默认初始化需要用户提供的默认构造函数",即使不需要构造函数。因此,由于缺陷报告253目前没有拟议的措辞,clang对此缺乏支持,并表示(强调我):

8.5的第9段[dcl.init]说:

如果没有为对象指定初始值设定项,并且该对象属于(可能是cv限定的)非POD类类型(或其数组)对象应默认初始化如果对象属于const限定类型,基础类类型应具有用户声明的默认构造函数。否则,如果没有初始化项为对象指定,对象及其子对象(如果有)具有不确定的初始值;如果对象或其任何子对象是const限定类型,程序格式不正确

如果常量POD对象没有非静态数据成员,该怎么办?对于这种情况,这种措辞需要一个空的初始值设定项

[…]

类似的注释适用于非POD常量对象,其所有非静态数据成员和基类子对象都具有默认构造函数。为什么这样一个对象的类需要有一个用户声明的默认构造函数?

缺陷报告仍然开放,但最后一条评论是:

如果隐式默认构造函数初始化所有子对象,则不需要初始化器。

还注意:

鉴于constexpr构造函数和非静态数据成员初始值设定项,应该再次提出此问题。

请注意,自从缺陷报告出现以来,在8.5部分中,对const限定类型的限制一直在移动。这是由于建议N2762:在C++11之前的琐碎问题。

尽管缺陷报告仍然是打开的,但考虑到constexpr的更改和非静态数据成员初始化程序,它似乎不再是必要的限制。特别是考虑到以下对constexpr构造函数的要求:

  • 应初始化每个非可变非静态数据成员和基类子对象(12.6.2)