21

这是对这个问题的跟进。
如答案的评论中所述:

内联变量具有以下特性 -它在每个翻译单元中具有相同的地址。[...] 通常你通过在 cpp 文件中定义变量来实现这一点,但是使用 inline 说明符你可以在头文件中声明/定义你的变量,并且每个使用这个内联变量的翻译单元都使用完全相同的对象。

此外,从答案本身来看:

虽然该语言不保证(甚至提及)跨共享库边界使用此新功能时会发生什么,但它确实适用于我的机器。

换句话说,当涉及共享库时,不清楚内联变量是否保证跨边界唯一。有人凭经验证明它适用于某些平台,但它不是正确的答案,它可能会破坏其他平台上的一切。

当跨边界使用内联变量时,它的唯一性是否有任何保证,或者它只是一个我不应该依赖的实现细节?

4

3 回答 3

9

这就是我对标准的解释。根据basic.link/1

一个程序由一个或多个链接在一起的翻译单元组成。

它没有说任何关于静态链接或动态链接的内容。程序是链接在一起的翻译单元。链接是否分两步完成并不重要(首先创建一个 .dll/.so,然后动态链接器将所有动态库 + 可执行文件链接在一起)。

因此,在我的解释中,无论程序是动态链接还是静态链接,实现都应该表现相同:类静态变量应该是唯一的(无论它是否内联)。

在 Linux 上,这是真的。

在 Windows 上,这并不适用于所有情况,因此在我的解释中,它在这些情况下违反了标准(如果您创建一个单独的 .dll,其中包含静态、非内联变量以及所有其他 .dll 和exe 指的是这个变量,它可以工作)。

于 2018-07-17T07:53:41.660 回答
6

C++ 目前没有共享库的概念。因此,inline跨共享库的行为方式将是特定于实现和平台的。

[basic.link]/1声明“一个程序由一个或多个链接在一起的翻译单元组成。 ”这一事实并不完全意味着与另一个已经链接的模块链接在一起的程序应该表现相同。

多年来,提出了很多整改方案(N1400N1418N1496N1976N2407N3347N4028),但都没有成功。只是很难以通用的方式实现,而且 C++ 通常会尝试避开实现细节。正如 GCC所说

对于不支持 COMDAT 或弱符号的目标,大多数具有模糊链接的实体都作为本地符号发出,以避免来自链接器的重复定义错误。然而,内联中的局部静态不会发生这种情况,因为拥有多个副本几乎肯定会破坏事情。

MSVC 默认不公开任何符号。任何“外部”符号都需要使用特定于平台的__declspec(dllexport). 因此,不能声称 Windows 与 C++ 不兼容。这里没有违反任何 C++ 规则,因为没有任何规则。

于 2018-07-19T07:45:50.180 回答
1

当跨边界使用内联变量时,它的唯一性是否有任何保证,或者它只是一个我不应该依赖的实现细节?

由您来确保这一点(通过确保所有声明实际上都是相同的)。

编译器显然无法检查这一点,链接器也不会打扰。因此,如果您对链接器撒谎(执行上述操作),那么您最终会遇到麻烦。


好的,因为不是每个人都明白我所说的“对链接器撒谎”的意思,我会稍微充实一下。

@oliv 好心地提供了这个链接,其中包括这个(我的评论):

这些结构的重复副本[即在多个 TU 中内联声明的变量] 将在链接时被丢弃。

这很好,这就是我们需要的。问题是,您不知道哪些(显然,只保留了一个,因此扩展,您不知道将是哪个)。

所以,如果它们不同,你不知道你最终会得到哪个,所以你最终得到的是(一种特别阴险的)UB。这就是我所说的“对链接器撒谎”的意思。因为,通过在不同的 TU 中以不同的方式声明变量,这正是您所做的。哎呀!

于 2018-07-17T06:38:20.213 回答