全局作用域中变量和函数的递归依赖关系

4

我在使用C++编译器进行编程时,注意到一个全局变量和函数调用之间的递归依赖关系没有被拒绝。例如:

#include <iostream>

int foo();

int t = foo();

int foo()
{
    std::cout << "Hello before main: " << t << "\n";
    t = 10;
    return t + 10;
}

int main()
{
  std::cout << "Hello from main.\n";
}

这个已编译的程序会输出以下内容:

Hello before main: 0
Hello from main.

当我声明变量 t 时,它依赖于函数 foo,而函数 foo 又依赖于变量 t。C++ 编译器通过将 t 在赋值表达式运行之前初始化为零来打破这个循环。

这让我感到惊讶。这是根据标准的正确行为,还是我在这里调用了一些未定义的行为?

1个回答

4
自C++20起,对象的生命周期被认为是在其初始化完成时开始;在此之前访问它会导致未定义行为。

[basic.life]/1:

...当一个类型为T的对象的生命周期开始时:

  • 获得适合类型T的正确对齐和大小的存储空间,并且
  • 它的初始化(如果有)完成(包括虚无初始化)...

直到C++20,这是明确定义的行为。零初始化首先针对非本地变量进行。

非本地初始化所述,静态和线程局部变量(自C++14以来)未被常量初始化在任何其他初始化之前都会被零初始化。如果非类非本地变量的定义没有初始化程序,则默认初始化不执行任何操作,使早期的零初始化结果保持不变。

从标准中,[basic.start.static]/2

如果没有进行常量初始化,则具有静态存储期([basic.stc.static])或线程存储期([basic.stc.thread])的变量将被零初始化([dcl.init])。零初始化和常量初始化一起称为静态初始化;所有其他初始化都是动态初始化。所有静态初始化都在任何动态初始化之前强烈发生([intro.races])。


@LanguageLawyer 我不太确定,但这是否意味着t的生命周期仅在所有初始化(包括此情况下的零初始化和复制初始化)完成后才开始? - songyuanyao
目前它的含义是,在动态初始化完成之前访问对象是未定义行为,但据我所知,这被认为是一个缺陷。 - Language Lawyer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接