内联函数和一个定义规则

inline functions and the one definition rule

本文关键字:一个 定义 规则 函数      更新时间:2023-10-16
inline

函数在某种程度上削弱了一个定义规则 - 允许多个定义,尽管有一些限制。 我在网上找到的一个措辞是

要求是每个定义应该相同,这意味着它应该包含相同的标记并引用相同的项目。

虽然我承认我不知道这是否是确定的。 我也不确定它有多严格。

我正在考虑使用class定义和/或inline函数创建标头的情况,无论它们是在 C++03、C++11 甚至更高标准中编译,我都希望能够#include。 使用__cplusplus宏有条件地更改代码是很自然的,但 ODR 会发挥作用吗? 例如,有条件地:

  • 提供嵌套typedef
  • 提供嵌套class
  • 提供与移动相关的功能。
  • 将函数标记为throw()noexcept。 (这一点尤其重要,因为每个析构函数都会在 C++11 中选取一个隐式noexcept
  • 将函数标记为constexpr
  • override和/或final标记函数。
  • 将函数标记为[[noreturn]]和/或[[nodiscard]]
  • 将参数标记为[[maybe_unused]]
  • 在函数体中使用[[fallthrough]]

但是,如果想要使#include此类标头的库能够在不同的标准下编译并且仍然可以安全地一起使用,那么实际上允许其中的哪一个(如果有的话)是允许的?

一般来说,你不能安全地做这些事情。只有两种方法可以安全地使用类的两个定义。简单来说,您可以简单地将两个单独的进程以不同的方式编译,这些进程通过共享内存等方式进行通信。在以下情况下,您可以使用两个以两种不同方式定义相同符号 A 的库:

  • 符号 A 只是库的实现细节;它不能由库提供,也不能出现在任何接口中
  • 按照这些思路,库的任何头文件都不应传递包含 A 的头文件。因此,客户端翻译单元不会从库中接收 A 的任何定义。
  • 符号 A 的可见性必须标记为私有/隐藏。

如果你做到了所有这些,那么 A 确实是库的实现细节,你可以使用多个以不同方式定义 A 的库。如果其中任何一个不满意,那么您不能保证上述任何一项都会起作用(尽管有些会)。

对于那些不熟悉链接器的人来说,最令人惊讶的结果之一是,如果 lib1 和 lib2 都使用符号 A,即使它们阻止了通过标头的任何泄漏,如果 A 的可见性是公开的,那么 A 的单个定义将在两个库中使用。因此,lib2 将使用 lib1 的定义,反之亦然。这很容易导致 UB。

在 *nix 系统上,公众可见性是默认设置,因此您需要确保特意隐藏符号,这有点晦涩难懂。