内联函数和一个定义规则
inline functions and the one definition rule
inline
函数提供了一种弱化的单一定义规则——允许多重定义,尽管有一些限制。我在网上找到的一种说法是
The requirements are that each definition should be the same, meaning it should consist of the same tokens and refer to the same items.
虽然我承认我不知道这是否是确定的。我也不确定它有多严格。
我正在考虑创建 headers 和 class
定义 and/or inline
函数的情况,我想 #include
-able 是否一种是用 C++03、C++11 甚至是后来的一些标准进行编译。使用 __cplusplus
宏来有条件地更改代码是很自然的,但是 ODR 会起作用吗?例如,有条件地:
似乎是合理的
- 提供嵌套
typedef
。
- 提供嵌套
class
.
- 提供 move-related 功能。
- 标记函数
throw()
或 noexcept
。 (这一点特别重要,因为在 C++11 中每个析构函数都会选择一个隐式的 noexcept
。)
- 标记一个函数
constexpr
。
- 用
override
and/or final
. 标记函数
- 标记一个函数
[[noreturn]]
and/or [[nodiscard]]
.
- 标记一个参数
[[maybe_unused]]
.
- 在函数中使用
[[fallthrough]]
body。
但是,如果想要启用 #include
等 headers 的库可以在不同标准下编译并且仍然可以安全地一起使用,那么实际上允许其中哪些(如果有的话)?
一般来说,您无法安全地执行任何这些操作。只有两种方法可以安全地使用 class 的两个定义。简单地说,你可以简单地让两个独立的进程以不同的方式编译,通过例如共享内存。更简单的是,您可以使用两个以两种不同方式定义相同符号 A 的库,如果:
- 符号A只是库的一个实现细节;它不能由图书馆提供,也不能出现在任何界面中
- 按照这些思路,库的 header 文件中的 none 应该传递地包含 A 的 header。因此客户端翻译单元不会从库中收到 A 的任何定义。
- 必须标记符号A的可见性private/hidden。
如果你做到了所有这些,那么 A 确实是库的一个实现细节,你可以使用多个以不同方式定义 A 的库。如果其中任何一个不满足,那么你不能保证以上任何一个都有效(尽管有些会)。
对于那些不熟悉链接器的人来说,最令人惊讶的结果之一是,如果 lib1 和 lib2 都使用符号 A,即使它们阻止了通过 header 的任何泄漏,如果 A 的可见性是 public 那么 A 的单一定义将在两个库中使用。所以 lib2 将使用 lib1 的定义,反之亦然。这很容易导致 UB。
在 *nix 系统上,public 可见性是默认设置,因此您需要确保隐藏符号,这有点神秘。
inline
函数提供了一种弱化的单一定义规则——允许多重定义,尽管有一些限制。我在网上找到的一种说法是
The requirements are that each definition should be the same, meaning it should consist of the same tokens and refer to the same items.
虽然我承认我不知道这是否是确定的。我也不确定它有多严格。
我正在考虑创建 headers 和 class
定义 and/or inline
函数的情况,我想 #include
-able 是否一种是用 C++03、C++11 甚至是后来的一些标准进行编译。使用 __cplusplus
宏来有条件地更改代码是很自然的,但是 ODR 会起作用吗?例如,有条件地:
- 提供嵌套
typedef
。 - 提供嵌套
class
. - 提供 move-related 功能。
- 标记函数
throw()
或noexcept
。 (这一点特别重要,因为在 C++11 中每个析构函数都会选择一个隐式的noexcept
。) - 标记一个函数
constexpr
。 - 用
override
and/orfinal
. 标记函数
- 标记一个函数
[[noreturn]]
and/or[[nodiscard]]
. - 标记一个参数
[[maybe_unused]]
. - 在函数中使用
[[fallthrough]]
body。
但是,如果想要启用 #include
等 headers 的库可以在不同标准下编译并且仍然可以安全地一起使用,那么实际上允许其中哪些(如果有的话)?
一般来说,您无法安全地执行任何这些操作。只有两种方法可以安全地使用 class 的两个定义。简单地说,你可以简单地让两个独立的进程以不同的方式编译,通过例如共享内存。更简单的是,您可以使用两个以两种不同方式定义相同符号 A 的库,如果:
- 符号A只是库的一个实现细节;它不能由图书馆提供,也不能出现在任何界面中
- 按照这些思路,库的 header 文件中的 none 应该传递地包含 A 的 header。因此客户端翻译单元不会从库中收到 A 的任何定义。
- 必须标记符号A的可见性private/hidden。
如果你做到了所有这些,那么 A 确实是库的一个实现细节,你可以使用多个以不同方式定义 A 的库。如果其中任何一个不满足,那么你不能保证以上任何一个都有效(尽管有些会)。
对于那些不熟悉链接器的人来说,最令人惊讶的结果之一是,如果 lib1 和 lib2 都使用符号 A,即使它们阻止了通过 header 的任何泄漏,如果 A 的可见性是 public 那么 A 的单一定义将在两个库中使用。所以 lib2 将使用 lib1 的定义,反之亦然。这很容易导致 UB。
在 *nix 系统上,public 可见性是默认设置,因此您需要确保隐藏符号,这有点神秘。