根据我听到的信息,如果你有一个如下变量:
inline int a_var;
或者
struct Foo
{
static inline int a_var;
};
在头文件中并将该头包含在静态库 library2 中的某个位置,然后将其包含在库 Library2 中,然后每个库都会有该变量的副本。
所以我测试了一下。我制作了一个可执行文件并链接到静态库 1 和静态库 2。
我在一个公共头中的结构中声明一个变量:
struct MyStruct
{
static inline int static_var = 0;
};
在我的可执行文件/main()中我执行:
void library_one_function();
void library_two_function();
#include "common_header.h"
#include <iostream>
int main()
{
MyStruct::static_var = 1;
std::cout << "Value from executable = " << MyStruct::static_var << "\n"; // I GET 1
library_one_func(); // LIBRARY ONE FUNCTION CHANGES IT TO 7
std::cout << "Value from executable = " << MyStruct::static_var << "\n"; // I GET 7
library_two_function(); // LIBRARY TWO FUNCTION CHANGES IT TO 100
std::cout << "Value from executable = " << MyStruct::static_var << "\n"; // I GET 100
}
每次我引用该变量时,都没有迹象表明每个库都有自己的副本。总是一样。那么为什么人们说内联变量有自己的副本呢?
C++ 没有说。
C++ 确实承认单独编译和预编译库的可能性,但它没有规定任何细节,并且不允许它们的使用影响格式良好的 C++ 程序的行为或语义。库的参与和特性是与 C++ 语义无关的实现细节。
如果在给定的实现中,多个静态库各自获得了自己的
static
inline
数据成员副本,那么该实现的链接器就有责任尽一切必要努力来确保 C++ 规范...(类.静态.数据;C23 11.4.9.3/1)
... 已得到满足。可以通过以某种适当的方式将多个副本合并为一个副本,通过拒绝在发生冲突时进行链接,或者通过其他方式。
任何消息灵通的人说,这种重复在 C++ 程序中是可见的,并希望别人像你一样理解,这是在评论实现不一致的可能途径。在静态库的典型实现中不太可能观察到这种情况。共享库的某些实现可能存在更多风险,但这又是一个实现细节。
补充说明
在评论中,您引用了 Raymond Chen 针对不同情况所发表的一些评论,您对此评论如下:
这涉及内联函数的静态局部变量,而不是问题中示例中的静态内联变量。无论如何,尽管在表达这种不确定性时规范可能对此不清楚,但最新版本包含一条注释,澄清了这一点(C++23 版本):
(dcl.inline;C++23 9.2.8/6)
因此所需的语义是类似的:就 C++ 行为和语义而言,静态局部变量只有一个副本,无论函数是否以内联方式定义,也无论涉及多少个或何种库。
附加说明
当然,您可以简单地避免使用静态内联数据成员,也许还可以避免使用内联函数的静态局部变量。非内联静态数据成员(被访问的)必须只有一个定义 - 因此不在标头中 - 并且确保不会重复是必要的,而且相对容易。