单个TU中内联变量的静态初始化顺序

发布于 2025-01-30 03:58:08 字数 2698 浏览 2 评论 0 原文

我知道这个问题已被问到很多次,但这似乎是一个稍有不同的变化,我不知道。

考虑以下代码:

#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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文