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

Dav*_*veC 3 c++ gcc libstdc++ c++11

不要问我为什么要做我正在做的事情……那将是一个很长的故事。目前,这篇文章的目的是学习并理解为什么事情没有按照我预期的方式进行。也许我的期望是错误的?

  • 因此,最初我使用最新的编译器(例如gcc 10.2.0 )从源代码构建了自己的 SystemC 2.3.3 库。但是,为了保持与旧版 gcc 的向后兼容性,我请求 C++11 :

    ./configure CXXFLAGS="-DSC_CPLUSPLUS=201103L"
    
    Run Code Online (Sandbox Code Playgroud)
  • 接下来,我想使用支持 C++11(和相同 ABI)的旧版 gcc 构建一个应用程序,例如gcc 8.2.0

    g++ -std=c++11 sc_main.cpp -I/path/to/systemc/include -L/path/to/systemc/lib -lsystemc -lm -o sim
    
    Run Code Online (Sandbox Code Playgroud)

令我惊讶的是,链接失败:

libsystemc.so: undefined reference to `std::__cxx11::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >::basic_stringstream()
Run Code Online (Sandbox Code Playgroud)

实际上,比较的输出

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> >::"
Run Code Online (Sandbox Code Playgroud)

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> >::"
Run Code Online (Sandbox Code Playgroud)

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

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

Sam*_*hik 5

这是否意味着一般来说,库的生产者和使用者使用相同的 C++ 版本(以及 > 相同的 ABI)是必要的,但还不够?

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

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

namespace std {

    class string {
         char *chars;
         size_t nchars;
    public:
         // Constructors and other methods.
    };
}
Run Code Online (Sandbox Code Playgroud)

真实的std::string要复杂一些(并且会使用为 C++ 实现保留的符号名称,但这并不重要)。这只是一个简化的说明。std::string甚至在 C++11 之前就已经存在。所以,多年来,事情不断发展,您的 C++ 编译器已更新到 C++20。不管出于什么原因,它的 C++ 库决定稍微改变这个类:

namespace std {

    class string {
         size_t nchars;
         char *chars;
    public:
         // Constructors and other methods.
    };
}
Run Code Online (Sandbox Code Playgroud)

此时,您可以选择指示新的 C++ 编译器仅在 C++11 语言修订版上进行编译。这将仅允许 C++11 语言功能。但是,生成的二进制代码仍然无法与旧版本 C++ 编译器构建的代码进行二进制兼容,该编译器是使用不兼容的类布局进行编译的。

一般来说:为了使新编译器构建的 C++ 代码与旧编译器构建的代码二进制兼容,需要显式编译/配置选项。这当然有可能是指定通用 C++ 语言版本的选项,但仅仅这样做通常是不够的。所做的只是指示编译器使用哪种语言版本来编译 C++ 代码。较新的语言版本废弃/弃用了早期版本的功能,并且语言选项的目的是允许当前的C++ 编译器编译为早期版本的 C++ 标准编写的源代码。这与向后/向前兼容性不同。