C++默认初始化是否保留之前的零初始化?

10

如果具有静态存储期的对象的C++构造函数未初始化成员,是否需要保留先前的零初始化,还是将成员留有不确定值?

我对C++规范的理解是,它自相矛盾。

示例:

#include <iostream>

struct Foo { Foo(); int x; } object;

Foo::Foo() { }

int main() { std::cout << object.x << std::endl; }

Foo() 构造函数没有显式初始化成员对象x,因此根据12.6.2第8段中的注释:
“该成员具有不确定值。”
但是通过详细分析各种初始化的细节,这似乎是不正确的。member对象x由于具有静态存储期,因此进行了零初始化,然后我看不到任何改变它的方法。
关于构造函数,在12.6.2中适用的文本为:
“实体是默认初始化的。”
在8.5第7段中,默认初始化的相关情况是:
“...不执行任何初始化。”
我认为这意味着先前的零初始化不会被默认初始化更改。
我是否漏掉了重置所有成员在构造函数调用开始时返回"不确定值"的其他文本?
我在stackoverflow上找到了一些关于零初始化和默认初始化的其他问题,但我找不到任何分析当默认初始化跟随同一实体的早期初始化时会发生什么的问题。
在这种情况下,可能没有实际影响。但在更复杂的构造函数中,有些成员已初始化而其他成员没有,编译器是否必须精确跟踪初始化的字节/位?还是可以只初始化整个对象(例如,将构造函数简化为memset()调用)?

2
我移除了我的回答,因为有一个我不确定的问题:Foo 是一个具有非平凡初始化的对象,这意味着只有在其构造函数完成后才开始其生命周期。我不确定其成员变量的值。在构造函数开始执行之前,您甚至可能无法访问它们(按规范规则),所以我很难相信它们已经有初值(零)存储了..更别提首次存在对象。 - Johannes Schaub - litb
@LightnessRacesinOrbit 关于“如果只是草案,那么它不属于标准,不能依赖它”这一点,我认为我们写大多数真实世界的代码会很困难。考虑到有多少缺陷报告(其中一些仍然未解决)试图修复标准中的错误 :) 例如,http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#191 - Johannes Schaub - litb
1
我更倾向于这是没问题的。12.6.2p6也说如果成员没有初始化,那么其值就是不确定的。但它并没有说构造函数没有对其进行初始化。零初始化同样算作初始化,因此在我看来二者并不冲突。 - Johannes Schaub - litb
@ᐅJohannesSchaub-litbᐊ 我认为你在完全按字面意思阅读措辞方面可能是正确的。但是,在讨论构造函数执行的初始化时,隐含地引用所有初始化最多只会深混淆人心...此外,如果成员在构造函数调用之前已被赋值(而不是初始化),会怎样呢?例如,如果堆对象具有显式析构函数调用,然后是放置新对象,类似于https://dev59.com/Q2bWa4cB1Zd3GeqPXH3m - user1998586
这是有效的:S *p = malloc(sizeof*p); p->a = 1; new (p) S; /* p现在引用一个不同的新对象 */ int x = p->a; 但这是无效的:S *p = malloc(sizeof*p); int *pa = &p->a; *p = 1; new (p) S; /* p现在引用一个不同的对象,但pa已经失效并且没有被重新设置 */ int x = *pa; /* UB */。但请记住:我不是专家,我说的一切都可能是错的!规范中并不是非常清楚... - Johannes Schaub - litb
显示剩余11条评论
1个回答

4

缺陷报告1787导致N3914中记录的更改被应用于C++14草案标准。将[dcl.init]第12段从:

如果未为对象指定初始化程序,则对象将进行默认初始化;如果未执行初始化,则具有自动或动态存储持续时间的对象具有不确定值。[注意:具有静态或线程存储持续时间的对象将进行零初始化,请参见3.6.2。—注释]

变为:

如果一个对象没有指定初始化器,那么该对象将被默认初始化。当获得具有自动或动态存储期的对象的存储空间时,该对象具有不确定的值,如果未对该对象执行初始化,则该对象保留不确定的值,直到该值被替换(5.17 [expr.ass])。[注意:具有静态或线程存储期的对象进行零初始化,请参见3.6.2 [basic.start.init]。- 结束说明]如果评估产生不确定的值,则除以下情况外,行为是未定义的:
[...]
这使得不确定值的情况只发生在具有自动或动态存储期的对象上。由于这是通过缺陷报告应用的,因此它可能也适用于C++11,因为缺陷报告发生在C++14被接受之前,但它也可能适用于更早的版本。缺陷适用的范围从未对我来说很清楚。由于在评论中提到了放置new,因此相同的更改还修改了部分[expr.new],使不确定值部分成为注释:
如果省略了新初始化程序,则对象将进行默认初始化(8.5 [dcl.init]); 如果没有执行任何初始化,则对象具有不确定的值。[注:如果未执行任何初始化,则对象具有不确定的值。—end note]。
该部分的开头说:
[...]由new-expression创建的实体具有动态存储期(3.7.4)。[...]
这似乎足以应用[dcl.init]部分中的更改。
此更改也很有趣,因为在此更改之前,C ++标准中未定义术语indeterminate value

在这种情况下,新的措辞非常清晰。但对于placement-new(哪些存储期适用于规则?)来说,情况并不是那么清晰。我的理解是,任何先前的初始化都是无关紧要的,行为与自动/动态存储期相同。例如,如果您有一个静态持续时间的char数组,然后调用placement-new在数组中构造对象,则char数组的零初始化是无关紧要的吗? - user1998586

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接