1. ホーム
  2. c++

[解決済み] C++17、C++14、C++11のオブジェクトをリンクしても安全か?

2022-06-12 10:56:59

質問

3つのコンパイルされたオブジェクトがあるとします。 同じコンパイラ/バージョン :

  1. A は C++11 標準でコンパイルされています。
  2. B は C++14 標準でコンパイルされています。
  3. C は C++17 標準でコンパイルされています。

簡単のために、すべてのヘッダーが C++11 で書かれていたと仮定しましょう。 3 つの標準バージョン間でセマンティクスが変更されていない構造体のみを使用する場合

そのため、相互依存性はヘッダー インクルードで正しく表現され、コンパイラーは異議を唱えませんでした。

これらのオブジェクトのどの組み合わせが、単一のバイナリにリンクしても安全なのでしょうか、また安全ではないのでしょうか。なぜでしょうか。


EDIT: 主要なコンパイラ (例: gcc, clang, vs++) をカバーする回答が歓迎されます。

どのように解決するのですか?

これらのオブジェクトのどの組み合わせが、単一のバイナリにリンクしても安全なのでしょうか?なぜですか?

GCCのため は、オブジェクトA, B, Cのどんな組み合わせでもリンクすることが安全です。もし、それらがすべて同じバージョンでビルドされているなら、それらはABI互換で、標準バージョン(すなわち -std オプション) は何の違いもありません。

なぜでしょうか?なぜなら、これは私たちの実装の重要な特性であり、私たちはそれを確実にするために懸命に働いているからです。

問題があるのは、異なるバージョンのGCCでコンパイルされたオブジェクトを一緒にリンクする場合です。 でコンパイルされたオブジェクトをリンクした場合、新しいC++標準の不安定な機能を、その標準に対するGCCのサポートが完全でないうちに使ってしまった場合です。たとえば、GCC 4.9を使ってオブジェクトをコンパイルし -std=c++11 を使ってコンパイルし、別のオブジェクトをGCC 5と -std=c++11 を使用すると問題が発生します。C++11のサポートはGCC 4.xでは実験的でしたから、C++11の機能のGCC 4.9と5のバージョンの間で互換性のない変更があったのです。同様に、もし、あるオブジェクトをGCC 7でコンパイルし -std=c++17 でコンパイルし、別のオブジェクトをGCC 8と -std=c++17 というのは、GCC 7 と 8 の C++17 サポートはまだ実験的で発展途上のものだからです。

一方、以下のオブジェクトのどのような組み合わせでも動作します。 libstdc++.so のバージョンについては後述します)。

  • オブジェクト D は GCC 4.9 でコンパイルされ -std=c++03
  • オブジェクト E は GCC 5 でコンパイルされ -std=c++11
  • オブジェクト F は GCC 7 でコンパイルされ -std=c++17

これは、使用した 3 つのコンパイラー バージョンのすべてで C++03 サポートが安定しているため、すべてのオブジェクト間で C++03 コンポーネントに互換性があるためです。C++11 のサポートは GCC 5 以降安定していますが、オブジェクト D は C++11 の機能を使用しておらず、オブジェクト E と F はどちらも C++11 のサポートが安定しているバージョンを使用しています。C++17のサポートはどのコンパイラのバージョンでも安定していませんが、オブジェクトFだけがC++17の機能を使っているので、他の2つのオブジェクトとの互換性に問題はありません(唯一共有している機能はC++03またはC++11から来ており、使われているバージョンでそれらの部分は問題ありません)。後で4番目のオブジェクトGをGCC 8を使ってコンパイルし -std=c++17 を使用して 4 番目のオブジェクトである G をコンパイルしたい場合、F と G の C++17 シンボルは互換性がないため、同じバージョンで F を再コンパイルする必要があります (または F へのリンクを行わない)。

上記の D、E、F 間の互換性についての唯一の注意点は、プログラムでは libstdc++.so の共有ライブラリを使用しなければならないということです。オブジェクトFはGCC 7でコンパイルされたので、そのリリースの共有ライブラリを使う必要があります。なぜなら、プログラムのどの部分もGCC 7でコンパイルすると libstdc++.so には存在しないシンボルに依存する可能性があるからです。同様に、もし、GCC 8でビルドされたオブジェクトGにリンクした場合、GCC 8の libstdc++.so を使い、Gが必要とするすべてのシンボルが見つかることを確実にする必要があります。単純なルールは、プログラムが実行時に使用する共有ライブラリが、少なくともオブジェクトのいずれかをコンパイルするために使用されるバージョンと同じくらい新しいことを保証することです。

GCC を使用する際のもうひとつの注意点は、あなたの質問に対するコメントですでに言及されていますが、GCC 5 以降は の2つの実装があります。 std::string の二つの実装が libstdc++ で利用可能です。この 2 つの実装はリンク互換ではありませんが(マングル名が異なるので一緒にリンクできません)、同じバイナリに共存できます(マングル名が異なるので、あるオブジェクトで std::string を使い、もう一方が std::__cxx11::string ). もしあなたのオブジェクトが std::string を使用している場合、通常、それらはすべて同じ文字列の実装でコンパイルされるべきです。コンパイル時に -D_GLIBCXX_USE_CXX11_ABI=0 を選択すると、オリジナルの gcc4-compatible の実装、あるいは -D_GLIBCXX_USE_CXX11_ABI=1 を選択すると、新しい cxx11 の実装(名前に惑わされないでください、C++03 でも使用できます。 cxx11 と呼ばれるのは、それがC++11の要件に適合しているからです)。どの実装がデフォルトかは GCC がどのように設定されたかによりますが、デフォルトは常にマクロでコンパイル時に上書きすることが可能です。