如果我尝试编译this code
struct A {
const int j;
};
A a;
我会收到预期的错误:
错误:'struct A'中未初始化的const成员
但是,如果我尝试编译this one:
struct A {
const int j;
};
A * a = new A();
我会成功建立。
问题是:为什么new
分配允许使用const成员创建变量而没有显式构造函数和堆栈分配 - 不是吗?
答案 0 :(得分:10)
这不是因为堆分配,而是因为分配时使用的括号。如果你这样做。
A* a = new A;
它也会失败。
添加括号时它起作用的原因是因为那时你的结构是值初始化的,对于像A
这样的POD类型的值 - 初始化值 - 初始化每个成员,以及{的默认值初始化{1}}为零。
这意味着如果只添加值初始化括号,它可能也可以在堆栈上创建变量:
int
虽然这会带来其他潜在的问题,但如果可以的话,最好使用统一初始化(要求C ++ 11):
A a = A(); // watch out for the http://en.wikipedia.org/wiki/Most_vexing_parse
答案 1 :(得分:6)
从C ++ 14开始,这是不正确的。 §12.1[class.ctor]说
4类X的默认默认构造函数定义为已删除 如果:
- [...]
- 没有 brace-or-equal-initializer 的const-qualified类型(或其数组)的任何非变量非静态数据成员都没有 用户提供的默认构造函数,
- [...]
如果默认构造函数不是用户提供的,并且如果:
,则默认构造函数是微不足道的
- 其类没有虚函数(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 issue 1301改变。
编译器供应商通常会实现缺陷解决方案,因此可以将其视为GCC 4.8中已在4.9中修复的错误。