是一个可用作constexpr变量的空类,没有初始值设定项或显式默认构造函数
Is an empty class usable as a constexpr variable without an initializer or explicit default constructor?
给定以下代码:
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)
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 初始化具有非默认构造函数的std::数组项的更好方法
- 具有默认模板类型的默认构造函数的类型推导
- 如何使用非默认构造函数实例化模板化类
- 有没有一种代码密度较低的方法来使用非默认构造函数初始化数组?
- 声明没有默认构造函数的字段
- 没有默认构造函数作为模板参数的自定义比较器
- C++17 没有默认构造函数的地图放置(私有默认构造函数)
- 使用移动调用对等构造函数unique_ptr默认构造函数
- C++复制构造函数和默认构造函数
- 将向量从 N1 缩小到 N2 项,而不触发默认构造函数并仅使用 move 语义
- 为什么即使我调用参数化构造函数也会调用默认构造函数?
- 具有非默认构造函数的单例类
- 在 C++ 中声明 const 对象需要用户定义的默认构造函数.如果我有一个可变成员变量,为什么不呢?
- 如何处理没有默认构造函数但在另一个构造函数中构造的对象?
- 在C++中使用默认构造函数初始化对象的不同方法
- 在没有默认构造函数的情况下创建的派生对象
- 强制使用默认构造函数对成员进行未初始化的声明
- 使用默认构造函数初始化对象的不同方法
- 创建类类型的动态分配数组,其中类不得具有默认构造函数