【问题标题】:Why C++'s <vector> templated class doesn't break one definition rule?为什么 C++ 的 <vector> 模板类没有违反一个定义规则?
【发布时间】:2016-04-05 18:39:06
【问题描述】:

也许这是个蹩脚的问题,但我不明白! 如果我在多个翻译单元(不同的 .cpp)中包含 &lt;string&gt;&lt;vector&gt;,为什么它不会破坏 ODR? 据我所知,每个 .cpp 的编译方式都不同,因此将为每个目标文件分别生成向量的方法代码,对吧? 所以链接器应该检测到它并抱怨。 即使不会(我怀疑这是模板的特殊情况),当我将所有内容链接在一起时,它是否会在每个单元中使用一个代码或一组不同的克隆代码???

【问题讨论】:

  • 本质上,编译器和链接器共同使用内联函数使用的相同机制使其工作。
  • 您怀疑这是模板的特殊情况,例如内联函数。不同文件中的定义应该完全正确,不违反ODR。
  • barney:您为什么不尝试在 ODR 中挑选出您认为被违反的特定短语,以及为什么规定的例外情况不适用?

标签: c++ templates language-lawyer one-definition-rule


【解决方案1】:

任何模板定义不会破坏 ODR 的方式相同 — ODR 特别指出模板定义可以跨翻译单元重复,只要它们在字面上是重复的(并且,因为它们是重复,没有冲突或歧义是可能的)。

[C++14: 3.2/6]: 类类型(第 9 条)、枚举类型(7.2)、带外部链接的内联函数(7.1.2)、类模板(第 14 条)、非静态函数可以有多个定义模板 (14.5.6)、类模板的静态数据成员 (14.5.1.3)、类模板的成员函数 (14.5.1.1) 或未指定某些模板参数的模板特化 (14.7, 14.5.5)在程序中,前提是每个定义出现在不同的翻译单元中,并且定义满足以下要求[..]

在同一翻译单元中多次包含&lt;vector&gt; 被明确允许并有效地省略,“#ifndef”标题保护很可能。

【讨论】:

  • 我明白了...但是代码重复呢?矢量类包含代码(好吧,模板化和抽象,但仍然是代码......)。所以它会为我使用它的每个翻译单元中的每个包含生成代码,对吗?因此,即使我在任何地方都使用 std::vector ,也应该为每个模块单独生成非常相同的代码......这看起来并不理想......
  • @barney:是的,这是次优的。 C++ 的编译模型(大部分继承自 C)有其缺陷,模板的添加使它们变得更糟。这是 C++ 编译被视为非常慢的重要原因之一——它必须解析 每个 编译单元的每个定义。然后它必须解决所有这些重复并在链接时删除它们。没有人说这是可以做到的最好方式,只是说这是 C++ 的方式。 :)
  • @barney:链接器会在链接过程中删除所有重复项。
  • 编译器实际上会在每个需要它的编译单元中生成一个vector&lt;int&gt; 实现。有时这意味着内联代码(vector 中的大多数方法都非常轻量级),而其他时候则意味着独立函数。链接器将对其进行排序并删除独立函数的任何重复副本。
  • @barney:那么那将是一种不同的语言,不是吗
【解决方案2】:

ODR 并没有说明一个结构只会在所有编译单元中声明一次——它指出如果您在多个编译单元中声明一个结构,它必须是相同的结构。如果您有两个不同的 vector 类型名称相同但内容不同,则会违反 ODR。那时,链接器会感到困惑,并且您会混淆代码和/或错误。

【讨论】:

  • 这正是您修改标题中的定义但不重建包括该标题在内的所有翻译单元时发生的情况。
【解决方案3】:

该标准对模板有一个特殊例外,允许复制否则会违反 ODR 的函数(例如具有外部链接的函数和非内联成员函数)。来自 C++11 3.2/5:

如果 D 是一个模板并且在多个翻译单元中定义, 那么上述要求应同时适用于 模板定义中使用的模板的封闭范围(14.6.3), 以及实例化时的依赖名称(14.6.2)。如果 D 的定义满足所有这些要求,那么程序 应该表现得好像有一个 D 的定义。如果 D 的定义不满足这些要求,则行为 未定义。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-08
    • 1970-01-01
    • 2011-05-10
    • 2015-07-06
    相关资源
    最近更新 更多