以下代码是否有效,例如不会带来不确定的行为?
struct S
{
int i = s.i;
static S s;
};
S S::s;
int main()
{
S a; // a.i = 0
S::s.i = 42;
S b; // b.i = 42
}
据我所知,所有具有静态存储持续时间的变量都初始化为零。因此,s.i
在0
创建时就是S::s
,一切都很好。但是也许我想念一些东西。
答案 0 :(得分:6)
我认为定义明确。
静态数据成员的初始化和销毁方式与 非局部变量。
[basic.start.static]/2 (强调我的意思)
用于变量或临时对象o的常量初始化程序是 初始化程序,其完整表达式是一个常量表达式,除了 如果o是一个对象,那么这样的初始化器也可以调用constexpr o及其子对象的构造函数,即使这些对象属于 非文字类类型。 [注意:此类可能有微不足道的 析构函数。 — [注释]。 具有静态或线程存储持续时间的变量或临时对象为 由实体的常量初始化程序初始化。如果恒定 不执行初始化,带有静态存储的变量 持续时间或线程存储持续时间为零初始化。 一起, 零初始化和常量初始化称为静态 初始化;所有其他初始化是动态初始化。 所有静态初始化都强烈发生在([intro.races])任何动态初始化之前。 [注意:动态初始化 非局部变量在[basic.start.dynamic]中进行了描述;的 局部静态变量在[stmt.dcl]中进行了描述。 —尾注]
[dcl.init]/6 (强调我的意思)
将对象或类型T的引用零初始化意味着:
- 如果T是标量类型,则将该对象初始化为通过将整数常量0(零)转换为T获得的值;
- 如果T是(可能是经过cv限定的)非工会类类型,则每个非静态数据成员,每个非虚拟基类子对象,以及 该对象不是基类的子对象,每个虚拟基类 子对象被初始化为零,填充被初始化为零位;
- 如果T是(可能是cv限定的)联合类型,则对象的第一个非静态命名数据成员将初始化为零,而填充为 初始化为零位;
- 如果T是数组类型,则每个元素都将初始化为零;
- 如果T是引用类型,则不执行初始化。
由于int i = s.i;
意味着s.i
进行了动态初始化,因此可以保证预先将其初始化为零。因此,当以后将其用于初始化自身时,其值将不会是不确定的。预期为0。
答案 1 :(得分:2)
您缺少什么。将具有静态存储持续时间的变量清零,然后将其构造函数称为。
我不能完全确定S.i
的值为S.i
的初始化是未定义的行为(因为此时S.i
尚未初始化)是不是(因为必须为零)。
编辑:Defect Report 2026中的代码实际上与此相似,并且被声明为格式错误(这意味着编译器必须出错)。我怀疑委员会的意图是OP的代码是不确定的行为。
编辑2:上面的DR引用了constexpr
值。这可能会改变无关紧要的事情。
曾经说过:如果您依靠非常仔细地阅读标准来使代码合法,那么您就依靠编译器作者来仔细阅读它。您可能是正确的,但是如果编译器作者误读并实施了其他操作(尽管希望最终他们会修复该错误),这在短期内无济于事。