以下代码是否有效,例如不会带来未定义的行为?
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
一切都很好。但也许我错过了一些东西。
以下代码是否有效,例如不会带来未定义的行为?
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
一切都很好。但也许我错过了一些东西。
我认为它定义明确。
静态数据成员的初始化和销毁与非局部变量完全一样。
[basic.start.static]/2(强调我的)
变量或临时对象 o 的常量初始值设定项是其完整表达式为常量表达式的初始值设定项,除非 o 是对象,这样的初始值设定项还可以为 o 及其子对象调用 constexpr 构造函数,即使这些对象不属于-literal 类类型。[注意:这样的类可能有一个重要的析构函数。— end note ] 如果具有静态或线程存储持续时间的变量或临时对象由实体的常量初始化程序初始化,则执行常量初始化。如果不执行常量初始化,则对具有静态存储持续时间或线程存储持续时间的变量进行零初始化。零初始化和常量初始化合称为静态初始化;所有其他初始化都是动态初始化。所有静态初始化都强烈发生在 ([intro.races]) 任何动态初始化之前。[注:非局部变量的动态初始化在[basic.start.dynamic]中有描述;[stmt.dcl] 中描述了局部静态变量。——尾注]
[dcl.init]/6(强调我的)
对 T 类型的对象或引用进行零初始化意味着:
- 如果 T 是标量类型,则将对象初始化为通过将整型文字 0(零)转换为 T 获得的值;
- 如果 T 是(可能是 cv 限定的)非联合类类型,每个非静态数据成员,每个非虚拟基类子对象,并且,如果对象不是基类子对象,每个虚拟基类子对象为零-initialized 和 padding 被初始化为零位;
- 如果 T 是(可能是 cv 限定的)联合类型,则对象的第一个非静态命名数据成员初始化为零,填充初始化为零位;
- 如果 T 是数组类型,则每个元素都初始化为零;
- 如果 T 是引用类型,则不执行初始化。
因为int i = s.i;
meanss.i
是经过动态初始化的,所以保证事先初始化为零。所以当它稍后用于初始化时,它的值不会是不确定的。预期为 0。
你错过了一些东西。具有静态存储持续时间的变量被归零,然后调用它们的构造函数。
我不太清楚的是,S.i
使用 的值初始化是否S.i
是未定义的行为(因为S.i
此时尚未初始化)或不是(因为它必须为零)。
编辑:缺陷报告 2026中的代码与此非常相似,并且被声明为格式错误(这意味着编译器必须出错)。我怀疑委员会的意图是 OP 的代码是未定义的行为。
编辑 2:上述 DR 指的是constexpr
值。这可能足以改变无关紧要的事情。
话虽如此:如果您依靠非常仔细地阅读标准来使您的代码合法,那么您就是依靠编译器作者仔细阅读它。您可能是对的,但是如果编译器作者误读并实现了其他东西(尽管希望他们最终会修复该错误),这在短期内无济于事。