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

Sha*_*our 9 c++ language-lawyer constexpr c++11 c++14

给出以下代码:

struct f {
};

int main(){
    constexpr f f1 ;
    //const f f1  ; // This also has the same issue
    //constexpr f f1 = {} ; //This works
}
Run Code Online (Sandbox Code Playgroud)

clang和gcc不同意它是否有效,clang提供以下诊断(见现场直播):

error: default initialization of an object of const type 'const f' without a user-provided default constructor
constexpr f f1 ;
            ^
              {}
Run Code Online (Sandbox Code Playgroud)

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

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

constexpr f() {} ;
Run Code Online (Sandbox Code Playgroud)

答案变化f是不是聚合?

Sha*_*our 7

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

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

那是f字面型吗?

3.9 [basic.types]节说:

类型是文字类型,如果它是:

并涵盖以下项目中的类:

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

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

所以我们对第一和第三个子弹都没问题.为了覆盖第二个项目符号,我们可以注意到这f是一个聚合但是如果我们稍微修改一下这个例子,例如f看起来像这样:

struct f {
   private:
      int x = 0 ;
} ;
Run Code Online (Sandbox Code Playgroud)

它不会是C++ 11或C++ 14中的聚合,但问题仍然存在.然后我们需要证明它有一个constexpr构造函数.是否f有constexpr构造?

据我所知,12.1 [class.ctor]部分说是的:

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

但遗憾的是,我们需要按部分提供用户提供的构造函数,8.5其中说:

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

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

8.5 [dcl.init]第9段说:

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

如果const POD对象没有非静态数据成员怎么办?这种措辞需要一个空的初始化器来处理这种情况

[...]

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

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

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

并且还注意到:

应根据constexpr构造函数和非静态数据成员初始化程序再次提出此问题.

请注意,8.5自缺陷报告出现以来,对const限定类型的约束在部分中移动了.这是由于提案N2762:不是那么琐碎的问题,这是微不足道的 C++ 11之前的问题.

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

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