【问题标题】:Are inline variables unique across boundaries?内联变量是否跨边界唯一?
【发布时间】:2018-12-24 18:04:14
【问题描述】:

这是this question的后续行动。
正如答案中的 cmets 所述:

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

另外,从答案本身来看:

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

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

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

【问题讨论】:

  • C++ 语言标准根本不知道诸如“共享库”之类的任何概念。因此,这是您的平台必须提供的保证。
  • @BoBTFish 这看起来像是一种精巧的表达方式 - 不,它不是。 :-) 如果您可以将其放在答案中并添加一些额外的细节,我们将不胜感激。谢谢。
  • @skypjack 每个链接器都确保内联变量的唯一性,否则实现不符合要求。内联变量的问题与内联函数本地的静态变量之一完全相同。这些局部静态变量的定义唯一性问题早就解决了。这被称为模糊链接 here
  • 这里来自一位非常可靠的作者的原始论文解释了同样的事情:open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4424.pdf
  • @ArneVogel 如果你能做到,这将是非常有启发性的,至少对我来说是这样。

标签: c++ shared-libraries language-lawyer c++17 inline-variable


【解决方案1】:

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

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

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

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

在 Linux 上,这是真的。

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

【讨论】:

  • 嗨@geza。我认为你的答案 + 我的答案 = 完整的答案,赞成。
  • 因此,换句话说(如果我错了,请纠正我),它应该可以按预期工作,但它不能在 Windows 上(这是我关心的平台),所以我不能依赖在这个解决方案上。对吗?
  • @skypjack:我想说你应该在 Windows 上试试这个。甚至,在 windows 上用 windows 标签询问有关此问题的新问题。希望这里有Windows专家,他们可以详细说明这个问题。但是,即使如此,您也应该尝试一下。过去,MSVC 以不符合标准而闻名。如今,它们试图符合标准,但也需要与旧的东西保持兼容。
  • @skypjack:但是,据我所知,如果您将静态变量放入 dll(而不是 exe),然后将此 dll 链接到其他 dll(使用该变量),你在 Windows 上也很好。
  • @skypjack 已经为 Windows 提供了答案:stackoverflow.com/q/19373061/1207195(至少在这种情况下,这与编译器的一致性无关)
【解决方案2】:

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

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

多年来已经提交了很多整改情况的建议(N1400N1418N1496N1976N2407N3347N4028),没有一个起飞。只是很难以通用的方式实现,而且 C++ 通常会尽量避开实现细节。作为 GCC put it:

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

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

【讨论】:

  • 这里没有违反任何 C++ 规则,因为没有。起首。 +1
【解决方案3】:

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

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

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


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

@oliv 好心地提供了this link,除此之外,它这么说(我的评论):

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

这很好,这就是我们需要的。问题是,你不知道是哪一个(显然,只有一个被保留,所以延伸一下,你不知道会是哪一个)。

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

【讨论】:

  • 如果您提供更多关于确保这一点对链接器撒谎意味着什么的详细信息,它将对未来的读者(实际上是我)有所帮助。也许有一些例子?您介意详细说明您的答案吗?谢谢。
  • 大声笑。好的,完成了,谢谢,好点子。 ......现在我已经改进了一点,适合我在早餐前匆匆忙忙。
  • 没问题。谢谢你这么有耐心。我现在有个问题。你说 - 由你来确保这一点(通过确保所有声明实际上都是相同的)。这意味着如果我将内联变量的声明放在仅标头库的 .hpp 文件中,它可以跨边界正常工作,然后我从可执行文件和由可执行文件本身链接的共享库的翻译单元中包含它?考虑到该库可能是稍后由其他人创建的。
  • 不用担心。耐心是一种美德,我自己做的不是很好,所以总是欢迎有机会练习。我相信你的问题的答案是肯定的,但如果你有任何疑问,我会敲定一个快速测试程序来确定。
  • 好吧,这个答案在明确解决问题方面没有任何进展。跨图书馆边界的唯一性假设是否无疑得到标准的保证?就是那个问题。我在这里看到的只是“[...] 我相信,是的”。是的,我也这么认为。但这不是 OP 想要的。他想要事实和保证。仅仅为每个平台测试它可能还不够,因为如果不能保证,那么编译器/链接器更新可能会在以后改变它的行为。这只是一个没有事实的仓促回答。对不起
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-23
  • 2018-05-21
  • 2010-10-29
  • 2017-01-08
  • 1970-01-01
相关资源
最近更新 更多