我试图确定N3337§8.5p7(C ++ 11)和N3797§8.5p8(后C ++ 11)之间的差异,这些差异涉及价值初始化。
N3337§8.5p7:
对T类型的对象进行值初始化意味着:
- 如果T是具有用户提供的构造函数(12.1)的(可能是cv限定的)类类型(第9节),那么T的默认构造函数 被调用(如果T无法访问,则初始化是错误的 默认构造函数);
- 如果T是一个(可能是cv限定的)非联合类类型而没有用户提供的构造函数,那么该对象是零初始化的,如果 T的隐式声明的默认构造函数是非平凡的 构造函数被调用。
- 如果T是数组类型,则每个元素都是值初始化的;
- 否则,该对象为零初始化。
值初始化的对象被视为构造并因此受到约束 适用于“建造”的本国际标准的规定 对象,“构造函数已完成的对象”等,甚至 如果没有为对象的初始化调用构造函数。
N3797§8.5p8:
对T类型的对象进行值初始化意味着:
- 如果T是一个(可能是cv-quali fi ed)类类型(第9条),没有默认构造函数(12.1)或默认构造函数 用户提供或删除,然后该对象被默认初始化;
- 如果T是(可能是cv-quali fi ed)类类型而没有用户提供或删除的默认构造函数,则该对象为零初始化 并检查默认初始化的语义约束, 如果T有一个非平凡的默认构造函数,那么对象就是 缺省初始化;
- 如果T是数组类型,则每个元素都是值初始化的;
- 否则,该对象为零初始化。
值初始化的对象被视为构造和 因此,本国际标准的规定适用于 “构造”对象,“构造函数具有的对象” 完成,“等等,即使没有为对象调用构造函数 初始化。
鉴于这两条规则,下面的代码段会给出不同的结果:
#include <iostream>
struct Base {
int i;
Base(int i):i(i) {}
Base():i(10) {}
};
struct Derived : public Base {
int j;
Derived(int j):Base(j), j(j) {}
Derived()=default;
};
int main() {
Derived d{};
std::cout << "d.i = " << d.i << " " << "d.j = " << d.j << '\n';
}
如下:
Derived
的默认构造函数,因为Derived
具有用户提供的构造函数。 Derived
的默认构造函数调用Base
默认构造函数,初始化Derived::i = 10
,保留Derived::j
一致。Derived
没有用户提供的默认构造函数,也没有删除的默认构造函数,因此第二个项目符号点适用。也就是说,Derived
是零初始化的,即Derived::i
和Derived::j
都用0初始化,对象d
默认初始化,离开{{1} }。虽然我对Unixs的了解最少,但我一直在尝试复制这两种情况,在Coliru中通过反复试验使用编译器clang ++和g ++的不同标志,但无济于事。到目前为止的结果,所有打印Derived::i = 10
都没有警告。
答案 0 :(得分:3)
OP中的程序无法区分d.j
是否被初始化为0或者它是否未初始化且恰巧恰好为0.如果要在内存中创建相关的Derived对象,这将是明确的已经初始化为已知的非零值,比如放置新的:
Derived d{42}; // d.i and d.j are both 42.
::new (&d) Derived{}; // d.i is 0, d.j is 0 per N3797 or 42 per N3337.
作为dyp says in his comment,编译器通常会跟踪由于标准中的缺陷(而不是新功能)而导致的更改,并将它们包含在对特定标准修订版的支持中。鉴于标准不断变化,可能没有编译器能够完全编译完全任何给定标准文档中指定的语言。当你告诉,例如,clang 3.4编译C ++ 11时,它实际实现的语言是“C ++ 11的一部分加上我们已经实现的相关缺陷解决方案(IIRC全部为3.4)及时发布3.4版本。“
OP询问的值初始化措辞的特定更改发生在Core Working Group (CWG) Defect Report (DR) number 1301的解决方案中,该解决方案也解决了DR1324和DR1368。作为缺陷解决方案,编译器将有理由实施更改。
使用各种编译器和版本进行分析(主要由OP执行)演示:
总之,没有办法强制编译器按照指定执行完全,但我们通常可以通过仔细分析来确定正在发生的事情。