如何理解在某些情况下允许实现将非局部变量的动态初始化视为静态初始化?

xsk*_*xzr 5 c++ static-initialization

其实问题出在标准草案N4582中的一句话:

[basic.start.static/3] 允许实现将具有静态或线程存储持续时间的变量初始化为静态初始化,即使此类初始化不需要静态完成,前提是

— 初始化的动态版本在初始化之前不会更改任何其他静态或线程存储持续时间对象的值,并且

— 如果所有不需要静态初始化的变量都被动态初始化,则初始化的静态版本在初始化变量中产生的值与动态初始化产生的值相同。

这些话是不是意味着如果满足这两个条件,类类型的非局部变量可能会被完全静态初始化(零初始化),这样它的构造函数就不会被调用(因为动态版本,通过调用构造函数初始化,可能被静态版本取代)?

Leo*_*eon 3

静态初始化在编译/链接期间执行。编译器/链接器在静态内存中为变量分配一个位置,并用正确的字节填充它(字节不需要全为零)。当程序启动时,静态内存的这些区域将从程序的二进制文件中加载,并且不需要进一步的初始化。

例子:

namespace A {
    // statically zero-initialized
    int a;
    char buf1[10];

    // non-zero initialized
    int b = 1;
    char date_format[] = "YYYY-MM-DD";
}
Run Code Online (Sandbox Code Playgroud)

与静态初始化不同,动态初始化需要在程序启动后运行一些代码,以将初始化的变量设置为其初始状态。需要运行的代码不需要是构造函数调用。

例子:

namespace B {
    int a = strlen(A::date_format);   (1)
    int b = ++a;                      (2)

    time_t t = time();                (3)

    struct C {
        int i;

        C() : i(123) {}
    };

    C c;                              (4)

    double s = std::sqrt(2);          (5)
}
Run Code Online (Sandbox Code Playgroud)

现在,C++ 标准允许编译器执行在动态初始化期间执行的计算,前提是这些计算没有副作用。此外,这些计算不能依赖于外部环境。在上面的例子中:

(1) 可以静态执行,因为strlen()没有任何副作用。

(2) 必须保持动态,因为它会发生变化a

(3) 必须保持动态,因为它依赖于外部环境/进行系统调用。

(4)可以静态地进行。

(5) 有点棘手,因为浮点计算取决于 FPU 的状态(即舍入模式)。如果告诉编译器不要那么认真地对待浮点运算,那么它可以静态地执行。