我知道这个问题已被问到很多次,但这似乎是一个稍有不同的变化,我不知道。
考虑以下代码:
#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或更高版本,这增加了对外部静态初始化内联变量的支持。我试图在使用内联指定符“ Out Out of Fine”时试图理解初始化顺序的保证。请注意, 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
应该被“部分订购”:因为它们是inline https://eel.is/c+++draft/basic.start.dynamic#1
然后,由于其中至少一个是部分排序的(另一个不是另一个是无序),按定义的顺序初始化: https:// https:/// eel.is/c++draft/basic.start.dynamic#3.1
也许我正在误读部分有序和无序的定义? IS V1
未按部分排序,因为内联指定符在定义较晚的IE中出现。该订单仅适用于声明中的内联?在这种情况下,我仍然看不到它将如何无序;另一种可能性是有效的。还需要指定内联违规行为,因此它似乎正在做 。 (我从上述情况中发现了此错误,但是 testValue
和 calltest
及其各自的定义是在多个标题上分配的, calltest
标题包括<代码> testValue )。
我还发现,GCC尊重 v1
和 ct
的定义顺序,而如上所述,Clang始终首先初始化 ct
。
eding :Clang中的另一个观察结果 - 如果我进行 v1
和 ct
consterexpr (删除构造函数的副作用), v1
的地址小于 ct
- 它们以预期顺序初始化。
我在上面的示例中也意识到 const inline calltest ct {}
具有内部链接,而 v1
是外部的静态成员。我修复了此问题, ct
仍然“错误地”初始化。不确定这是否会影响预期的初始化顺序。
我还运行了此测试:
extern const TestValue v1;
const inline TestValue v1{};
extern const inline CallTest ct{};
首先初始化 v1
。我不明白为什么,如果 v1
是静态类变量,则其初始化顺序与作为普通外部变量的初始化顺序不同。
I'm aware this question has been asked many times, but this seems to be a slightly different variation which I can't figure out.
Consider the following code:
#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() {}
I am using C++17 or later, which adds support for extern, static initialized inline variables. I am trying to understand the guarantees around initialization order when using the inline specifier "out of line". Notice that v1
is declared as a static variable of TestValue, then defined inline later, but before ct
. Surprisingly (to me at least), using Clang 14.0.3, the program prints:
CallTest Initalized at 0x404059
v_ptr = (nil)
TestValue Initialized at 0x404068
If I move v1
out of TestValue
such that it is declared and defined in the same line just before ct
, I get the expected output:
TestValue Initialized at 0x404059
CallTest Initalized at 0x404068
v_ptr = 0x404059
I am reasonably confident in my understanding of the standard that this second example is guaranteed to print TestValue first. But what about the first case?
I wasn't sure about the legality of forward declaring v1
, then defining it inline later, but that alone seems OK: https://eel.is/c++draft/dcl.inline#note-2
As for the ordering, my understanding is that v1
and ct
should be "partially ordered": since they are inline https://eel.is/c++draft/basic.start.dynamic#1
Then, since at least one of them is partially ordered (and the other is not unordered), they are initialized in the order of their definitions: https://eel.is/c++draft/basic.start.dynamic#3.1
Perhaps I'm misreading the definition of partially-ordered and unordered? Is v1
not partially-ordered since the inline specifier comes later in the definition - ie. the ordering only applies to inline at the declaration? In this case I still don't see how it would become unordered; the other possibility is ordered which works. Also specifying inline is needed to fix ODR violations, so it appears to be doing something. (I discovered this error from the above situation but where TestValue
and CallTest
and their respective definitions were split across multiple headers, the CallTest
header including TestValue
).
I also find that GCC respects the definition order of v1
and ct
while as above, Clang always initializes ct
first.
Godbolt Link
Edit: Another observation in Clang - If I make v1
and ct
constexpr (Removing the side effects from the constructors), the address of v1
is smaller than ct
- they are initialized in the expected order.
I also realized in the above example that const inline CallTest ct{}
has internal linkage, while v1
is external being a static member. I fixed this, and ct
is still "incorrectly" initialized first. Not sure if this is affects the expected initialization order.
I also ran this test:
extern const TestValue v1;
const inline TestValue v1{};
extern const inline CallTest ct{};
Which initializes v1
first. I don't understand why, if v1
is a static class variable, it would have a different initialization order than as an ordinary extern variable.
发布评论
评论(1)
看起来应该在Clang 16:
Demo: https://clang.godbolt.org/z /vmrhttw8v
Looks like this should be fixed in Clang 16: https://github.com/llvm/llvm-project/commit/f9969a3d28e738e9427e371aac06d71269220123
Demo: https://clang.godbolt.org/z/vMrhTTW8v