【问题标题】:In-class initialization from static member of the same type从相同类型的静态成员进行类内初始化
【发布时间】:2018-08-02 13:49:28
【问题描述】:

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

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 创建,一切都很好。但也许我错过了什么。

【问题讨论】:

  • 我认为你是对的,这段代码是正确的。
  • 无论如何我都会避免有问题的代码
  • @Slava 大部分带有标签language-lawyer 的代码都是有问题的

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


【解决方案1】:

我认为它的定义很明确。

[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。

【讨论】:

  • 这个逻辑表明,一个裸露的int i = i; 必然会产生一个值为 0 的 int。DR2026 表示它的格式不正确。
  • @MartinBonner 我相信这表明static int i = i; 为零。
  • @StoryTeller 我认为让我感到困惑的是 c++14 中的行为发生了变化。来自 cppreference :“在 [(until C++14)][而不是 (since C++14)] 零初始化之后执行持续初始化”
  • @FrançoisAndrieux - 确实。这段代码(和我的答案)的有效性取决于int i = s.i 是动态初始化,这使得 C++14 的变化无关紧要。如果它被归类为其他任何东西,那么我认为它也是 UB。
【解决方案2】:

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

我不能完全确定S.i 的初始化是否为S.i 的值是未定义的行为(因为此时S.i 尚未初始化)或不是(因为它必须为零)。


编辑:Defect Report 2026 中的代码在效果上与此非常相似,并且被声明为格式错误(这意味着编译器必须出错)。我怀疑委员会的意图是 OP 的代码是未定义的行为。

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

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

【讨论】:

  • 这也是我一直在脑海中闪过的东西。如果我们知道它被归零,那么后面的默认初始化是否真的会产生一个不确定的值。
  • constexpr 使 DR 完全不同以至于无关紧要。
  • 我没有看到constexpr
  • constexpr int i = i; - 在 DR 本身中
  • @StoryTeller 是的,我刚刚意识到这一点。哦!
猜你喜欢
  • 2020-01-31
  • 2023-03-18
  • 2011-07-18
  • 2015-05-18
  • 1970-01-01
  • 2011-01-19
  • 2013-07-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多