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

时间:2018-08-02 13:49:29

标签: c++ initialization language-lawyer static-members in-class-initialization

以下代码是否有效,例如不会带来不确定的行为?

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.i0创建时就是S::s,一切都很好。但是也许我想念一些东西。

2 个答案:

答案 0 :(得分:6)

我认为定义明确。

  

[class.static.data]/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值。这可能会改变无关紧要的事情。

曾经说过:如果您依靠非常仔细地阅读标准来使代码合法,那么您就依靠编译器作者来仔细阅读它。您可能是正确的,但是如果编译器作者误读并实施了其他操作(尽管希望最终他们会修复该错误),这在短期内无济于事。