提问者:小点点

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


给定以下代码:

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不是聚合?


共1个答案

匿名用户

如果我们从草案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]说是:

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

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

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

    因此,这里看起来clang是正确的,如果我们看一下下面的clang bug报告:“错误:const类型'const Z'的对象的默认初始化需要用户提供的默认构造函数”,即使不需要构造函数。因此,clang认为,由于缺陷报告253目前没有提议的措辞,他们对此缺乏支持,该报告说(重点是我的):

    8.5[dcl.init]第9段说:

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

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

    [...]

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

    缺陷报告仍然开放,但最后的评论说:

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

    并注意到:

    应该根据constexpr构造函数和非静态数据成员初始值设定项再次提出这个问题。

    请注意,自从缺陷报告出现以来,第8.5节中对const-qualified类型的限制已经改变。这是由于提案N2762:与C 11之前的琐碎问题无关。

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

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

  • 相关问题