答えには2つの部分があります。コンパイラーレベルでの互換性とリンカーレベルでの互換性。前者から始めましょう。
すべてのヘッダーがC ++ 11で記述されているとしましょう
同じコンパイラを使用すると、ターゲットのC ++標準に関係なく、同じ標準ライブラリヘッダーとソースファイル(コンパイラに関連付けられた1回)が使用されます。したがって、標準ライブラリのヘッダーファイルは、コンパイラがサポートするすべてのC ++バージョンと互換性があるように記述されています。
つまり、翻訳単位のコンパイルに使用されるコンパイラオプションが特定のC ++標準を指定している場合、新しい標準でのみ使用できる機能にはアクセスできません。これは__cplusplus
ディレクティブを使用して行われます。ベクトルを見る使い方の興味深い例ソースファイルを。同様に、コンパイラーは、新しいバージョンの標準によって提供される構文機能を拒否します。
つまり、想定は、記述したヘッダーファイルにのみ適用されるということです。これらのヘッダーファイルは、異なるC ++標準を対象とする異なる翻訳単位に含まれている場合、非互換性を引き起こす可能性があります。これについては、C ++標準の付録Cで説明されています。4つの条項があります。最初の条項についてのみ説明し、残りは簡単に説明します。
C.3.1条項2:字句の規則
単一引用符は、C ++ 11では文字リテラルを区切りますが、C ++ 14およびC ++ 17では数字の区切り文字です。純粋なC ++ 11ヘッダーファイルの1つに次のマクロ定義があるとします。
#define M(x, ...) __VA_ARGS__
// Maybe defined as a field in a template or a type.
int x[2] = { M(1'2,3'4) };
ヘッダーファイルを含むが、それぞれC ++ 11とC ++ 14をターゲットとする2つの変換単位を検討します。C ++ 11を対象とする場合、引用符内のコンマはパラメーターの区切り文字とは見なされません。パラメータは1つだけです。したがって、コードは次と同等になります。
int x[2] = { 0 }; // C++11
一方、C ++ 14を対象とする場合、一重引用符は桁区切り記号として解釈されます。したがって、コードは次と同等になります。
int x[2] = { 34, 0 }; // C++14 and C++17
ここでのポイントは、純粋なC ++ 11ヘッダーファイルの1つで一重引用符を使用すると、C ++ 14/17をターゲットとする翻訳単位に驚くべきバグが発生する可能性があるということです。したがって、ヘッダーファイルがC ++ 11で記述されている場合でも、それが標準の新しいバージョンと互換性があることを保証するために、慎重に記述する必要があります。の__cplusplus
ディレクティブは、ここでは有用である可能性があります。
標準の他の3つの条項は次のとおりです。
C.3.2条項3:基本概念
変化する:新しい通常の(配置されない)デアロケーター
根拠:サイズの割り当て解除に必要です。
元の機能への影響:有効なC ++ 2011コードでは、グローバル配置割り当て関数と割り当て解除関数を次のように宣言できます。
void operator new(std::size_t, std::size_t);
void operator delete(void*, std::size_t) noexcept;
ただし、この国際標準では、演算子の削除の宣言は、事前定義された通常の(配置されない)演算子の削除(3.7.4)と一致する場合があります。その場合、クラスメンバーの割り当て関数と割り当て解除関数(5.3.4)の場合と同様に、プログラムの形式が正しくありません。
C.3.3条項7:宣言
変更:constexpr非静的メンバー関数は暗黙的にconstメンバー関数ではありません。
根拠:constexprメンバー関数がオブジェクトを変更できるようにするために必要です。
元の機能への影響:有効なC ++ 2011コードは、この国際標準でコンパイルできない場合があります。
たとえば、次のコードはC ++ 2011では有効ですが、同じ国際標準では無効です。これは、同じメンバー関数を異なる戻り値の型で2回宣言しているためです。
struct S {
constexpr const int &f();
int &f();
};
C.3.4節27:入出力ライブラリ
変更:取得が定義されていません。
根拠:getの使用は危険と見なされます。
元の機能への影響:この国際規格では、gets関数を使用する有効なC ++ 2011コードはコンパイルに失敗する場合があります。
C ++ 14とC ++ 17の間の潜在的な非互換性については、C.4で説明します。非標準のヘッダーファイルはすべてC ++ 11で記述されているため(質問で指定されているとおり)、これらの問題は発生しないため、ここでは触れません。
次に、リンカーレベルでの互換性について説明します。一般に、非互換性の潜在的な理由は次のとおりです。
- オブジェクトファイルの形式。
- プログラムの起動および終了ルーチンと
main
エントリポイント。
- プログラム全体の最適化(WPO)。
結果のオブジェクトファイルの形式がターゲットのC ++標準に依存する場合、リンカーは異なるオブジェクトファイルをリンクできなければなりません。GCC、LLVM、およびVC ++では、幸いにもそうではありません。つまり、オブジェクトファイルのフォーマットは、ターゲット標準に関係なく同じですが、コンパイラ自体に大きく依存しています。実際、GCC、LLVM、およびVC ++のどのリンカーも、ターゲットのC ++標準に関する知識を必要としません。これは、コンパイル済みのオブジェクトファイルをリンクできることも意味します(ランタイムを静的にリンクします)。
プログラムの起動ルーチン(を呼び出す関数main
)がC ++標準ごとに異なり、異なるルーチンが互いに互換性がない場合、オブジェクトファイルをリンクすることはできません。GCC、LLVM、およびVC ++では、幸いにもそうではありません。さらに、の署名main
関数(およびそれに適用される制限、標準のセクション3.6を参照)はすべてのC ++標準で同じであるため、どの翻訳単位が存在するかは関係ありません。
一般に、WPOは、異なるC ++標準を使用してコンパイルされたオブジェクトファイルではうまく機能しない可能性があります。これは、コンパイラのどのステージがターゲット標準の知識を必要とするか、どのステージが必要としないか、およびオブジェクトファイルをまたぐプロシージャ間の最適化に与える影響に正確に依存します。幸い、GCC、LLVM、VC ++は適切に設計されており、この問題はありません(私が認識していることではありません)。
したがって、GCC、LLVM、およびVC ++は、C ++標準の異なるバージョン間でのバイナリ互換性を可能にするように設計されています。ただし、これは規格自体の要件ではありません。
ちなみに、VC ++コンパイラは、C ++標準の特定のバージョンをターゲットにできるstdスイッチを提供していますが、C ++ 11のターゲットはサポートしていません。指定できる最小バージョンはC ++ 14です。これは、Visual C ++ 2013 Update 3以降のデフォルトです。古いバージョンのVC ++を使用してC ++ 11をターゲットにすることもできますが、別のVC ++コンパイラを使用する必要があります。 C ++標準のさまざまなバージョンを対象とするさまざまな翻訳単位をコンパイルします。これにより、少なくともWPOが機能しなくなります。
警告:私の答えは完全ではないか、非常に正確ではないかもしれません。