使用最新的 gcc 生成库并使用较旧的 gcc 使用它 - 尽管 C++ 版本相同,但为什么会出现问题?

Producing a library with a recent gcc and consuming it with an older gcc - Why are there issues despite the same C++ version?

不要问我为什么要做我正在做的事情...那将是一个很长的故事。 目前,这个 post 的目的是学习和理解为什么事情不像我期望的那样工作。可能我的期望是错误的?

令我惊讶的是,link 失败了:

libsystemc.so: undefined reference to `std::__cxx11::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >::basic_stringstream()

实际上,比较

的输出
nm --demangle `/path/to/gcc/10.2.0/bin/g++ --print-file-name libstdc++.so` | grep "std::__cxx11::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >::"

nm --demangle `/path/to/gcc/8.2.0/bin/g++ --print-file-name libstdc++.so` | grep "std::__cxx11::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >::"

揭示了一些差异。实际上,前者包含 std::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >::basic_stringstream() 而后者不包含

这是预期的吗? 这是否意味着一般来说,库的生产者和消费者使用相同的 C++ 版本(和相同的 ABI)是必要的,但还不够?或者还有其他我不明白的事情吗?

Does it mean that in general, it is necessary but not sufficient for the producer and the consumer of a library to use the same C++ version (and the > same ABI) ?

正确。 Backwards/forwards 兼容性不仅仅由编译源代码时使用的 C++ 语言版本定义。 Backwards/forwards 兼容性本身就是一个复杂的话题。但我只会举一个简单的人为示例来说明一些基本概念。

让我们简化一下 std::string 是什么。它基本上是一个指针,字符串中的字符数:

namespace std {

    class string {
         char *chars;
         size_t nchars;
    public:
         // Constructors and other methods.
    };
}

真正的 std::string 稍微复杂一些(并且会使用为 C++ 实现保留的符号名称,但这无关紧要)。这只是一个简化的说明。 std::string 甚至在 C++11 之前就存在了。因此,多年来,事情不断发展,您的 C++ 编译器已更新到 C++20。无论出于何种原因,其 C++ 库决定稍微更改此 class:

namespace std {

    class string {
         size_t nchars;
         char *chars;
    public:
         // Constructors and other methods.
    };
}

此时您可以选择指示您的新 C++ 编译器仅在 C++11 语言修订版上进行编译。这将只允许 C++11 语言特性。然而,生成的二进制代码仍然不会 binary-compatible 与旧版本的 C++ 编译器构建的代码,该编译器是使用不兼容的 class 布局编译的。

一般来说:为了使新编译器构建的 C++ 代码与旧编译器构建的代码二进制兼容,需要一个明确的 compilation/configuration 选项。这当然是可能的,这可能是指定通用 C++ 语言版本的选项,但仅仅这样做通常是不够的。所做的只是指示编译器使用哪个语言版本来编译 C++ 代码。较新的语言版本 obsolete/deprecate 具有较早版本的功能,语言选项的目的是 允许为较早版本的 C++ 标准编写的源代码 current C++ 编译器编译。这与 backwards/forwards 兼容性不同。