如果我尝试编译此代码
struct A {
const int j;
};
A a;
Run Code Online (Sandbox Code Playgroud)
我会得到一个预期的错误:
错误:'struct A'中未初始化的const成员
但是,如果我尝试编译这个:
struct A {
const int j;
};
A * a = new A();
Run Code Online (Sandbox Code Playgroud)
我会得到一个成功的构建.
问题是:为什么new分配允许使用const成员创建一个没有显式构造函数和堆栈分配的变量 - 不是吗?
Som*_*ude 10
这不是因为堆分配,而是因为您在分配时使用的括号.如果你这样做
A* a = new A;
Run Code Online (Sandbox Code Playgroud)
它也会失败.
添加括号时它起作用的原因是因为那时你的结构是值初始化的,对于POD类型的A值初始化值 - 初始化每个成员,默认值初始化int为零.
这意味着如果你只是添加value-initialization括号,它可能也可以在堆栈上创建变量:
A a = A(); // watch out for the http://en.wikipedia.org/wiki/Most_vexing_parse
Run Code Online (Sandbox Code Playgroud)
虽然这带来了其他潜在的问题,但如果可以的话,最好使用统一初始化(要求C++ 11):
A a{};
Run Code Online (Sandbox Code Playgroud)
从C++ 14开始,这是不正确的.§12.1[class.ctor]说
4如果出现以下情况,则将类X的默认默认构造函数定义为已删除:
- [...]
- const-qualified类型(或其数组)的任何非变量非静态数据成员,没有brace-or-equal-initializer,没有用户提供的默认构造函数,
- [...]
如果默认构造函数不是用户提供的,则默认构造函数是微不足道的,如果:
- 它的类没有虚函数(10.3),也没有虚基类(10.1),和
- 没有类的非静态数据成员有一个大括号或等于初始化器,和
- 它的所有直接基类都有简单的默认构造函数,和
- 对于类类的所有非静态数据成员(或其数组),每个这样的类都有一个普通的默认构造函数.
§8.5[dcl.init]反过来说
7默认初始化T类型的对象意味着:
- 如果T是(可能是cv限定的)类类型(第9节),则调用T的默认构造函数(12.1)(如果T没有默认构造函数或重载解析(13.3),则初始化是错误的模糊性或在初始化上下文中删除或无法访问的函数中;
- [...]
8要初始化T类型的对象,意味着:
- 如果T是一个(可能是cv限定的)类类型(第9条),没有默认构造函数(12.1)或者是用户提供或删除的默认构造函数,那么该对象是默认初始化的;
- [...]
空的括号对导致值初始化.默认构造函数A定义为每[class.ctor]/p4删除.因此,通过[dcl.init]/p8,值初始化意味着默认初始化,并且通过p7,初始化是错误的,因为删除了构造函数.
C++ 11版本实际上允许在此上下文中进行值初始化; 它说价值初始化,除其他外,
如果T是一个(可能是cv限定的)非联合类类型而没有用户提供的构造函数,那么该对象是零初始化的,如果T的隐式声明的默认构造函数是非平凡的,则调用该构造函数.
因为根据上面的定义,默认构造函数是微不足道的(即使它被删除),它实际上并没有被调用.这被认为是标准中的缺陷,CWG问题1301改变了相关措辞.
编译器供应商通常会实现缺陷解决方案,因此这可能被视为GCC 4.8中已在4.9中修复的错误.