【问题标题】:Header-only libraries and multiple definition errors仅标头库和多个定义错误
【发布时间】:2011-04-27 18:10:20
【问题描述】:

我想写一个库来使用,你只需要包含一个头文件。但是,如果您有多个源文件并在两者中都包含标头,则会出现多个定义错误,因为该库既在标头中声明又在标头中定义。我认为,在 Boost 中,我已经看到了只有标头的库。他们是怎么做到的?

【问题讨论】:

  • 你在使用包含防护吗?编辑:看看 sams 的答案
  • @smerlin:看看 sams 上的 cmets 答案。

标签: c++ header multiple-definition-error


【解决方案1】:

Boost 主要是仅标题的主要原因是因为它非常面向模板。模板通常从单一定义规则中获得通过。事实上,要有效地使用模板,您必须让定义在使用该模板的任何翻译单元中可见。

解决单一定义规则 (ODR) 的另一种方法是使用 inline 函数。实际上,inline 真正做到的是从 ODR 获得免费通行证 - 事实上它可能内联函数实际上更多的是可选的副作用。

最后一个选择(但可能不是那么好)是让你的函数静态化。如果链接器无法确定所有这些函数实例确实相同,这可能会导致代码膨胀。但我提到它是为了完整性。请注意,编译器通常会内联 static 函数,即使它们没有标记为 inline

【讨论】:

  • 嗯,Boost 文档引用了大部分库的仅标头性质作为一项功能。只有那些不能合理地仅作为标头的东西(例如,正则表达式的东西具有操作系统依赖性)才不是标头。因此,尽管某些子库无疑只是因为它们是模板代码而成为标头,但将这个原因归咎于整个 Boost 是错误的,至少根据文档。
【解决方案2】:

有许多真正只有标头的 Boost 库,但它们往往非常简单(和/或只有模板)。更大的库通过一些技巧实现了相同的效果:它们具有“自动链接”(你会看到这个术语使用了here)。它们本质上在标头中有一堆预处理器指令,它们为您的平台找出适当的 lib 文件,并使用 #pragma 来指示链接器将其链接。所以您不必显式链接它,但它是仍在链接中。

【讨论】:

  • g++中自动链接的东西怎么用?我知道在 MSVC++ 中你可以使用 #pragma(lib, "blah.lib") 但你如何使用 g++ 来做同样的事情?
  • @Epro:恐怕我不知道它是怎么做到的。您可能需要查看 Boost 源代码。
【解决方案3】:

Boost 大量使用纯头文件库,因为与 STL 一样,它主要使用类和函数模板构建,它们几乎总是纯头文件。

如果您不编写模板,我会避免在您的头文件中包含代码 - 这比它的价值更麻烦。让它成为一个普通的旧静态库。

【讨论】:

    【解决方案4】:

    声明你的函数inline,并将它们放在一个命名空间中,这样你就不会发生冲突:

    namespace fancy_schmancy
    {
      inline void my_fn()
      {
        // magic happens
      }
    };
    

    【讨论】:

    • 但是,是否建议将 all 函数设为 inline 而不管其性质如何?我认为只有“短”函数应该是inline'd。
    • 忽略你的教授。他们不以编写代码为生。 “只应内联短函数”是一个经验法则,但有很多例外和许多其他更重要的原因,您应该或不应该内联函数以制定规则拇指几乎没用。
    • “过早的微优化是万恶之源”是不考虑内联的第一个原因。
    • @Arun:编译器不需要内联所有inline 函数,并且在您通过函数指针调用的情况下也不能。 inline 关键字暗示内联可能很好,但更重要的是它是一个链接修饰符。这是一个不幸的误称。无论如何,不​​要担心你的编译器会做任何愚蠢的事情。
    • @Arun: 不制作整个项目inlineinline 是一个有目的的工具(实际上是几个目的)。在这种情况下,并且在现在的大多数情况下,我们使用inline 的原因是为了满足 ODR——而不是作为性能调整。了解该工具、它适用于何处以及它的作用是什么,然后在适当的情况下使用它。
    猜你喜欢
    • 2012-01-02
    • 1970-01-01
    • 2012-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多