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

时间:2015-11-19 20:38:35

标签: c++ c++11 language-lawyer c++14 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提供以下诊断( see it live ):

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 个答案:

答案 0 :(得分: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 ;
} ;

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

据我所知,第12.1 [class.ctor] 表示是:

  

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

但遗憾的是,我们要求8.5部分提供用户提供的构造函数,其中包含:

  

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

所以看起来clang在这里是正确的,如果我们查看以下clang bug报告:"error: default initialization of an object of const type 'const Z' requires a user-provided default constructor" even when no constructor needed。因为defect report 253目前没有提议的措辞,并且它说(强调我的),所以clang基于他们缺乏对此的支持:

  

8.5 [dcl.init]第9段说:

     
    

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

  
     

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

     

[...]

     

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

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

  

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

还注意到:

  

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

请注意,自从出现缺陷报告以来,在8.5部分中对const限定类型的约束进行了移动。这是由于提案N2762: Not so Trivial Issues with Trivial是在C ++ 11之前。

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

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