C++ 使用不同版本的仅标题库是否会导致UB

C++ 使用不同版本的仅标题库是否会导致UB,c++,static-libraries,static-linking,C++,Static Libraries,Static Linking,假设我有一个库somelib.a,它由包管理器以二进制形式分发。并且该库使用仅标题库anotherlib.hpp 如果我现在将我的程序链接到somelib.a,并且使用anotherlib.hpp但版本不同,那么如果somelib.a在其include标题中使用anotherlib.hpp的一部分,这可能会导致UB 但是如果somelib.a仅在其cpp文件中引用/使用anotherlib.hpp,会发生什么情况(因此我不知道它是否使用了它们)?我的应用程序和somelib.a之间的链接步骤是否

假设我有一个库
somelib.a
,它由包管理器以二进制形式分发。并且该库使用仅标题库
anotherlib.hpp

如果我现在将我的程序链接到
somelib.a
,并且使用
anotherlib.hpp
但版本不同,那么如果
somelib.a
在其
include
标题中使用
anotherlib.hpp
的一部分,这可能会导致UB

但是如果
somelib.a
仅在其cpp文件中引用/使用
anotherlib.hpp
,会发生什么情况(因此我不知道它是否使用了它们)?我的应用程序和
somelib.a
之间的链接步骤是否确保
somelib.a
和我的应用程序都将使用自己的
anotherlib.hpp

我询问的原因是,如果我将程序的各个编译单元链接到最终程序,那么链接器将删除重复符号(取决于它是否为内部链接)。因此,只包含标题的库通常是以一种可以删除重复符号的方式编写的

一个简单的例子

somelib.a
是在nlohmann/json.hpp版本为3.2的系统上构建的

somelib/somelib.h

namespace somelib {
  struct config {
    // some members
  };

  config read_configuration(const std::string &path);
}
somelib.cpp

#include <nlohmann/json.hpp>


namespace somelib {
  config read_configuration(const std::string &path)
  {
     nlohmann::json j;
     std::ifstream i(path);

     i >> j;

     config c;

     // populate c based on j

     return c;
  }
}
#include <somelib/somelib.h>
#include <nlohmann/json.hpp>
#include <ifstream>

int main() {
   auto c = somelib::read_configuration("config.json");

   nlohmann::json j;
   std::ifstream i("another.json");

   i >> j;

   return 0;
}

使用静态库几乎没有什么区别

<> > C++标准说明如果在程序中<强>有内联函数(或类模板或变量等)的多个定义,并且所有定义不相同,则您有UB.< /P> 实际上,这意味着除非标题库的两个版本之间的更改非常有限,否则您将拥有UB。 例如,如果唯一的更改是空白更改、注释或添加新符号,则不会有未定义的行为。但是,如果现有函数中的一行代码被更改,那么它就是UB

从:

6.2一条定义规则 [……]

类别类型可以有多个定义(第12条), 枚举类型(10.2),带外部链接的内联函数 (10.1.6),带外部连杆的内联变量(10.1.6),等级 模板(第17条),非静态功能模板(17.5.6),静态 类模板的数据成员(17.5.1.3),类的成员函数 模板(17.5.1.1),或一些 如果每个定义出现在不同的翻译单元中,并且定义满足 以下要求

给定在多个翻译中定义的名为D的实体 单位,那么

  • D的每个定义应包含相同的定义 令牌序列;及

  • 在D的每个定义中,对应 根据6.4查找的名称应指定义的实体 在D的定义范围内,或指同一实体 重载分辨率(16.3)和部分模板匹配后 专业化(17.8.3),但名称可参考(6.2.1)

    • 一个非易失性常量对象,如果对象

      • 在D的所有定义中具有相同的文字类型, (6.2.1.2)

      • 使用常量表达式(8.20)初始化

      • odr是否用于D的任何定义,以及

      • 在D的所有定义中具有相同的值

    • 使用常量表达式初始化的具有内部链接或无链接的引用 这样,引用在所有定义中都引用同一实体 D;及(6.3)
  • 在D的每个定义中,对应的实体 应具有相同的语言链接;及

  • 在每个定义中 对于D,引用的重载运算符,隐式调用 转换函数、构造函数、运算符新函数和 操作员删除功能,应指相同的功能,或 D定义中定义的函数;及

  • 在每个定义中 D、 (隐式或显式)函数调用使用的默认参数 被视为其标记序列出现在的定义中 D也就是说,默认参数取决于需求 如本段所述(如果默认参数 具有默认参数的子表达式,此要求适用 递归地)

  • 如果D是一个隐式声明了 构造函数(15.1),就好像构造函数是隐式定义的 在每个使用odr的翻译单元中,以及 每个翻译单元中的定义应调用相同的构造函数 对于D的子对象

如果D是一个模板,并且在多个翻译单元中定义, 则上述要求应同时适用于 模板定义(17.6.3)中使用的模板封闭范围, 以及实例化时的从属名称(17.6.2)。如果 D的定义满足所有这些要求,然后行为 就好像只有一个D的定义一样。如果 如果不满足这些要求,则行为未定义。


这取决于库和编译器,如果库版本不兼容,那么是的,它将无法工作。提供@AlanBirtles是的,这是关于潜在的不兼容库版本。我更新了这个问题,让它包含一个类似mcve的东西,至少希望它能更清楚地说明我的问题。因此,如果一个库是作为静态库分发的,那么它必须用它们的版本记录所有库(包括仅在内部使用的头库),以便