从相同类型的静态成员进行类内初始化



以下代码有效吗,例如,不会带来未定义的行为?

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就是0S::s创建上,一切都很好。但也许我错过了什么。

我认为它定义得很好。

[class.static.data]/6

静态数据成员的初始化和销毁方式与非局部变量。

[基本启动静态]/2(强调矿(

变量或临时对象o的常量初始值设定项是其完整表达式是常量表达式的初始值设定项,除了如果o是一个对象,这样的初始值设定项也可以调用constexpro及其子对象的构造函数,即使这些对象属于非文字类类型。[注意:这样的类可能有一个非平凡的析构函数。—尾注]如果具有静态或线程存储持续时间的变量或临时对象为由实体的常量初始值设定项初始化。如果常量未执行初始化,具有静态存储的变量持续时间或线程存储持续时间初始化为零在一起,零初始化和常量初始化称为静态初始化;所有其他初始化都是动态初始化。所有静态初始化都强烈发生在([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。

您缺少了一些东西。具有静态存储持续时间的变量被归零,,然后它们的构造函数被调用

我不太清楚的是,用S.i的值初始化S.i是否是未定义的行为(因为S.i在这一点上没有初始化((因为它必须为零(。


编辑:缺陷报告2026中的代码在效果上与此非常相似,并被声明为格式错误(这意味着编译器必须出错(。我怀疑委员会的意图是OP的准则是不明确的行为。

编辑2:上述DR是指constexpr值。这可能会改变一些无关紧要的事情。

话虽如此:如果你依赖于对标准的仔细阅读来使你的代码合法,那么你就依赖于编译器作者对它的仔细阅读。你可能是对的,但如果编译器作者误读并实现了其他东西,这在短期内没有帮助(尽管希望他们最终能修复这个错误(。

最新更新