为啥 C++17 中的全局内联变量和静态内联成员需要守卫?

Posted

技术标签:

【中文标题】为啥 C++17 中的全局内联变量和静态内联成员需要守卫?【英文标题】:Why do global inline variables and static inline members in C++17 need guards?为什么 C++17 中的全局内联变量和静态内联成员需要守卫? 【发布时间】:2019-06-27 21:02:38 【问题描述】:

从 C++17 开始,可以使用 inline 关键字在标头中初始化全局变量和静态成员。虽然我理解为什么需要保护函数中的静态变量(因为即使在多线程上下文中初始化也应该只发生一次),但我不明白为什么这些新的内联变量也受到保护(你可以在这里看到:https://godbolt.org/z/YF8PeQ) .我认为在任何情况下,所有全局变量和静态成员的初始化都发生在程序执行开始时(甚至在main() 之前),因此此时无需考虑多个线程。请解释一下好吗?

【问题讨论】:

【参考方案1】:

每个包含定义并使用它的文件都会尝试初始化变量。即使这种情况是连续发生的,而不是同时发生的,您仍然需要一种方法来将变量标记为已初始化,这样只有第一次发生时会对其进行初始化,而稍后尝试初始化它不会做任何事情。

此外,您可以在main 启动之前拥有多个线程。全局变量的构造函数(以及这些构造函数调用的函数)可以产生新线程。

所以你可以有多段代码,都在main之前执行,都试图初始化同一个变量。这就是守卫的作用。

【讨论】:

“文件将尝试初始化”是什么意思?我认为初始化的事实应该由链接器解决,我真的不明白为什么链接器不能将不同对象文件中全局变量/静态成员的所有初始化“组合”成一个(就像他对内联全局函数定义所做的那样在标题中)并将其放在组件中的正确位置。以及这些线程中的代码如何可能初始化除了构造函数产生线程的变量之外的东西?它可以访问另一个全局(初始化与否,取决于顺序),但不能影响其初始化。 链接器无法为需要非平凡初始化的全局变量运行构造函数,这必须在运行时发生,在程序开始执行之后(但在main 开始之前)。 全局构造函数可以创建一个线程,该线程可以调用在不同对象文件中定义的函数,该函数可以触发该文件中定义的全局变量的初始化。 C++ 初始化比我想象的要复杂得多。 @JohnLettehw - 此外,在main() 启动之前运行的线程可以更改全局变量的值。在main() 之前运行的构造函数可以读取全局变量的值。因此,线程是否在构造函数运行之前执行此操作很重要。

以上是关于为啥 C++17 中的全局内联变量和静态内联成员需要守卫?的主要内容,如果未能解决你的问题,请参考以下文章

C++ 为啥要引入内联函数、、

纯虚函数可能没有内联定义。为啥?

成员函数与内联函数

为啥不允许“内联”静态常量,除了整数?

为啥内联未命名的命名空间?

类定义中的静态数据成员初始化?