显然模板库只需要标题,但对于非模板,你什么时候应该只做标题?
4 回答
如果您认为您的非模板库可能只是标头,请考虑将其分成两个文件,然后提供第三个文件,其中包含 the.h
和 the .cpp
(带有包含保护)。
然后,在许多不同的 TU 中使用您的库并怀疑这可能会花费大量编译时间的任何人都可以轻松地进行更改以对其进行测试。
一旦您知道用户可以选择使用库的方式,答案可能会变成“尽可能提供该选项”。几乎任何时候,从多个 TU 中包含它都不会违反 ODR。例如,如果您的非static
自由函数引用static
全局变量,那么您就不走运了,因为该函数在不同 TU 中的不同定义将引用相同名称的不同对象,这是违反 ODR 的。
你可以跟随 Boost.Asio 的领导。
他们只是提供了两个版本的库:header-only 和 header + library。
他们在包含标题之前使用要定义(或不定义)的单个宏来执行此操作。我认为默认(如果未定义)是使用仅标头版本。
请参阅可选的单独编译。
请注意它们如何巧妙地提供要编译的单个源文件,该文件定义了所有内容或链接到动态加载的库的选项。
模板库不需要只有头文件:实现很可能包含一些独立于模板参数的部分,并且由于某些原因(例如代码大小较小)被分成一个特殊的二进制文件。
我无法想象非模板库真的必须是仅标题的情况。然而,有时允许内联所有代码在性能方面可能是合理的。一个例子可以是围绕平台特定接口的包装库,例如同步原语、线程本地存储、平台和编译器特定的原子操作实现等。
如果没有模板,您将在标题中有实际定义。这意味着如果两个文件包含您的标头,您将获得多个定义并且代码将无法编译。
换句话说,将定义放在标题中是一个非常糟糕的主意。你应该只坚持声明和模板。
至于模板,编译器知道您可能多次包含相同的标头,它们不会一遍又一遍地生成相同的代码。
编辑:如果您的意思是“保持所有内容内联”,我认为这是一个非常糟糕的方法。头文件变得完全不可读,并且实现中的任何更改都会迫使库的任何用户重新编译所有内容。