C++ 模板的 ODR 规则有多严格?

问题描述 投票:0回答:1

假设我有一个复杂的项目 P,它使用库 A 和 B,并且这些库使用包含一些模板的仅标头库 T。

P --> A --> T(1.0)
|
 ---> B --> T(1.0)

T的作者发布了1.1版本,该版本在源代码级别向后兼容,即T的任何用户都可以使用新版本重新编译,并且程序将继续运行。假设 A 决定升级,但 B 保留 T 版本 1.0。

P --> A --> T(1.1)
|
 ---> B --> T(1.0)

我是否正确假设几乎任何对 T 的更改都会违反 ODR 规则,如下所述:https://en.cppreference.com/w/cpp/language/definition

可以有多个定义...只要满足以下所有条件: 每个定义都由相同的标记序列组成(通常, 出现在同一个头文件中)

实际上来说,是否存在技术上非法但实际上“安全”的更改?

  • 如果项目是静态链接的(liba.lib、libb.lib)?
  • 项目是否是动态链接的(a.so、b.so)?
c++ templates one-definition-rule
1个回答
0
投票

该标准认为定义内的任何词汇变化(就预处理后的标记而言)都违反了单一定义规则。没有例外。

因此,从这个角度来看,您始终必须重新编译程序中的每个翻译单元(无论是否在库中),其中包括具有修改后的定义的标头。

当然,实践中通常不是这样做的。

在实践中,人们通常可以依赖 ABI 规范或至少依赖 ABI 规范中的常见模式,还可以依赖编译器如何工作或在感兴趣的特定平台上链接如何工作的一般知识。有了这些知识,通常可以得出结论,即使头文件与翻译单元之间的旧版本混合在一起,某些更改也不会产生任何意想不到的后果。

不幸的是,没有简单的规则可以遵循。这将取决于您想要改变的究竟

举个简单的例子:添加一个具有新名称的非虚非特殊函数作为类成员通常就可以了。编译器和 ABI 的工作方式,这样做不会改变 ABI 级别的任何内容,并且由于名称是新的,因此不存在重载解析导致间接 ODR 违规的风险,例如一个内联函数定义恰好使用旧的重载解析结果,而同一函数的另一个定义恰好使用新的结果。

© www.soinside.com 2019 - 2024. All rights reserved.