"static initialization order fiasco"是 constexpr 变量的问题吗?

Is the "static initialization order fiasco" a concern for constexpr variables?

本文关键字:问题 变量 static initialization order fiasco constexpr      更新时间:2023-10-16

如果我在一个翻译单元中用非默认值初始化一个constexpr变量foo,然后在另一个翻译单元中用foo初始化另一个constexpr变量bar,是否可以在foo之前初始化bar,从而导致由零或默认初始化foo初始化的bar。 即与非 constexpr 情况(静态初始化顺序惨败生效)不同,将编译器和链接器分析依赖关系排序以保证正确的结果?

此外,constexpr 变量模板会受到怎样的影响?它们的初始化顺序在单个翻译单元中未定义。

C++17个标准答案优先。

更新:下面是一个最小的例子。它有效;这就是问题所在。在这一点上,我 99% 确定这是安全的,不会受到静态初始化顺序惨败 (TSIOF)的影响。但是,由于该问题的极端,阴险性质,我需要确认这是可以的。我相信这段代码不会受到 TSIOF 的影响,因为 y.h 包含在 x.h 订单中,ab在 x.cc 翻译单元中。但是,AFAIU有2个翻译单元:一个包含a,另一个包含b。此外,AFAI-sort-of-U 不会产生a错误的多重定义,因为静态关键字赋予了内部链接,但a仍然具有全局范围

编译方式:

clang++ -std=c++17 x.cc y.cc  #or g++

可能的输出:

in foo

可能的输出:

assertion failed (core dumped)

文件 x.cc:

#include "x.h"
int main(){ assert(b == 42); foo(); }

文件 x.h:

#pragma once
#include "y.h"
static constexpr int b = a+1;

文件 y.cc:

#include "y.h"
#include <iostream>
void foo(){
std::cout << " in foo n";
}

文件 Y.H:

#pragma once
static constexpr int a=41;
void foo();

这个程序保证输出in foo吗?

由于这个问题无法通过示例来回答,因此确实需要语言律师提供相关的标准报价

这个问题是关于跨翻译单元的 STIOF。关于模板变量翻译单元中 STIOF 的一个相关的、未回答的问题在这里

在您的示例中,不存在可能的问题,因为 y.cc 中的a与 x.cc 中的a变量不同。因此,没有发生交叉翻译单元链接。

事实上,constexpr 变量之间不可能有交叉翻译单元链接。constexpr的全部意义在于该值是在编译时计算的。

换句话说,constexpr 必须在我们仍在隔离编译翻译单元时解析为一个值。

因此,静态初始化顺序惨败隐式不适用于constexpr变量,并且标准无需提及在这种情况下该怎么做。

编辑:根据要求,该标准的相关部分是10.1.5(9)[dcl.constexpr]

在任何 constexpr 变量声明中,完整表达式 初始化应为常量表达式 (8.20)。

这导致 8.20 (1) [expr.const] 具有以下注释:

[ 注意:常量表达式可以在翻译过程中计算。

这是下一页半条款背后的理由,但本身就足以排除交叉翻译单元引用。