我知道这个问题已经被问了很多遍,但这似乎是一个稍微不同的变化,我无法解决。
考虑以下代码:
#include <cstdio>
struct TestValue;
inline const TestValue* v_ptr = nullptr;
struct TestValue {
static const TestValue v1;
TestValue() {
v_ptr = this;
printf("TestValue Initialized at %p\n", this);
}
};
struct CallTest {
CallTest() {
printf("CallTest Initalized at %p\n", this);
printf("v_ptr = %p\n", v_ptr);
}
};
const inline TestValue TestValue::v1{};
const inline CallTest ct{};
int main() {}
我正在使用C++17或更高版本,它增加了对extern、静态初始化内联变量的支持。我试图理解当使用inline修饰符“out of line”时关于初始化顺序的保证。请注意,v1
被声明为TestValue的静态变量,然后在ct
之前但在其定义内联。出乎意料的是(至少对我来说),在使用Clang 14.0.3时,程序输出:
CallTest Initalized at 0x404059
v_ptr = (nil)
TestValue Initialized at 0x404068
如果我将
v1
从TestValue
中移出,使其在ct
前的同一行声明和定义,那么就会得到预期的输出结果。TestValue Initialized at 0x404059
CallTest Initalized at 0x404068
v_ptr = 0x404059
我比较自信地认为第二个例子会保证先输出TestValue。但第一个例子呢?
我不确定提前声明v1,然后稍后内联定义它的合法性,但仅是这样似乎没问题:https://eel.is/c++draft/dcl.inline#note-2 至于顺序,我理解v1和ct应该是“部分有序”的:因为它们是内联的https://eel.is/c++draft/basic.start.dynamic#1 然后,由于它们中至少有一个是部分有序的(另一个不是无序的),它们将按照其定义的顺序进行初始化:https://eel.is/c++draft/basic.start.dynamic#3.1 也许我误读了部分有序和无序的定义?v1没有被部分有序吗,因为内联说明符在定义中稍后出现 - 即顺序仅适用于声明时的内联说明符?在这种情况下,我仍然看不出它如何变得无序;另一种可能性是有序的,这也行得通。另外指定内联是需要修复ODR违规的,因此似乎它正在做某些事情。(我从上述情况中发现了这个错误,但TestValue和CallTest及其各自的定义被分成多个头文件,CallTest头文件包括TestValue)。
我还发现GCC尊重v1和ct的定义顺序,而与上面相同,Clang总是先初始化ct。
extern const TestValue v1;
const inline TestValue v1{};
extern const inline CallTest ct{};
这初始化先进行v1
。我不明白为什么,如果v1
是一个静态类变量,它为什么会与普通的extern变量具有不同的初始化顺序。