假设我有三个编译对象,都由 相同的编译器/版本 生成:
- A 使用 C++11 标准编译
- B 使用 C++14 标准编译
- C 是使用 C++17 标准编译的
为简单起见,我们假设所有头文件都是用 C++11 编写的, _仅使用语义在所有三个标准版本之间都没有改变的结构_,因此任何相互依赖都可以通过头文件包含正确表达,并且编译器没有反对。
这些对象的哪些组合是它,链接到单个二进制文件是否安全?为什么?
编辑:欢迎回答涵盖主要编译器(例如 gcc、clang、vs++)
原文由 ricab 发布,翻译遵循 CC BY-SA 4.0 许可协议
对于 GCC ,将对象 A、B 和 C 的任意组合链接在一起是安全的。如果它们都使用相同的版本构建,那么它们是 ABI 兼容的,标准版本(即
-std
选项)不没什么区别。为什么?因为这是我们实施的一个重要属性,我们努力确保这一点。
如果您将使用不同版本的 GCC 编译的对象链接在一起, 并且 在 GCC 对该标准的支持完成之前使用了新 C++ 标准的不稳定功能,那么您遇到的问题是。例如,如果您使用 GCC 4.9 和
-std=c++11
编译一个对象,而另一个使用 GCC 5 和-std=c++11
的对象会出现问题。 C++11 支持在 GCC 4.x 中是实验性的,因此 GCC 4.9 和 5 版本的 C++11 特性之间存在不兼容的变化。同样,如果你用 GCC 7 和-std=c++17
编译一个对象,用 GCC 8 和-std=c++17
编译另一个对象,你会遇到问题,因为 GCC 7 和 8 中的 C++17 支持仍然存在实验和发展。另一方面,以下对象的任何组合都将起作用(尽管请参阅下面关于
libstdc++.so
版本的注释):-std=c++03
-std=c++11
-std=c++17
这是因为 C++03 支持在使用的所有三个编译器版本中都是稳定的,因此 C++03 组件在所有对象之间是兼容的。自 GCC 5 起,C++11 支持稳定,但对象 D 不使用任何 C++11 功能,对象 E 和 F 都使用 C++11 支持稳定的版本。 C++17 支持在任何使用的编译器版本中都不稳定,但只有对象 F 使用 C++17 特性,因此与其他两个对象没有兼容性问题(它们共享的唯一特性来自 C++03或 C++11,并且使用的版本使这些部分正常)。如果您稍后想使用 GCC 8 和
-std=c++17
编译第四个对象 G,那么您需要使用相同版本(或不链接到 F)重新编译 F,因为 F 中的 C++17 符号和 G 不兼容。对于上述 D、E 和 F 之间的兼容性,唯一需要注意的是您的程序必须使用来自 GCC 7(或更高版本)的
libstdc++.so
共享库。因为对象 F 是使用 GCC 7 编译的,所以您需要使用该版本中的共享库,因为使用 GCC 7 编译程序的任何部分可能会引入对来自 GCC 的libstdc++.so
中不存在的符号的依赖关系4.9 或 GCC 5。同样,如果您链接到使用 GCC 8 构建的对象 G,则需要使用 GCC 8 中的libstdc++.so
以确保找到 G 所需的所有符号。简单的规则是确保程序在运行时使用的共享库至少与用于编译任何对象的版本一样新。使用 GCC 时的另一个警告(在您的问题的评论中已经提到)是,由于 GCC 5,libstdc++ 中有 两种实现
std::string
可用。这两个实现不是链接兼容的(它们具有不同的重整名称,因此不能链接在一起)但可以共存于同一个二进制文件中(它们具有不同的重整名称,因此如果一个对象使用不要冲突std::string
和其他用途std::__cxx11::string
)。如果您的对象使用std::string
那么通常它们都应该使用相同的字符串实现进行编译。 Compile with-D_GLIBCXX_USE_CXX11_ABI=0
to select the originalgcc4-compatible
implementation, or-D_GLIBCXX_USE_CXX11_ABI=1
to select the newcxx11
implementation (don’t be fooled by the名字,也可以在C++03中使用,叫cxx11
因为它符合C++11的要求)。哪个实现是默认的取决于 GCC 的配置方式,但默认值总是可以在编译时用宏覆盖。