ODRというルールがC++にあります。このルールの基本は(私の理解から)、何かがあなたが望むだけ多くの宣言を持つことができますが、ただ一つの定義です。それはシンプルなようですが、テンプレートとインライン関数で、それを壊すのはかなり簡単です。
テンプレートでは、複数の定義が不可避です。同じテンプレートのインスタンス化は、それを使用するすべての翻訳単位で行われます。それは1つの定義ルールに反しているようですが、インラインおよびテンプレート化エンティティの場合、ルールが拡張されます。ここcppreference上の段落です:
限り、各 定義は 次の各の、異なる翻訳単位で表示されるように、プログラムに複数の定義が存在することができます:クラス型、列挙型、インラインクラス 外部リンク付きのインライン変数(C++ 17以降)、クラス テンプレート、非静的関数テンプレート、クラスの静的データメンバ テンプレート、クラステンプレートのメンバ関数、部分テンプレート 特化、以下のすべてが真である限り長く:
各定義は、(典型的には同じヘッダファイルに表示される)トークンの同一の配列からなる内部または付き定数ことを除いて、各定義は(過負荷解像度後に)同じエンティティを見つける内から
ネームルックアップを リンケージは、 ODRが使用されていない限り、異なるオブジェクトを参照することができ、すべての定義で同じ値を持ちます。
言語結合(定義内で定義されたものを参照する場合を除き)同じである変換、割当て、および割当て解除機能を含む
オーバーロードオペレータは、各
定義と同じ機能を指す(例えばインクルードファイルは、定義が暗黙的に宣言されたコンストラクタを持つクラスは、すべての翻訳のためである場合は、上記の
3つのルールがそれぞれの定義
で使用されるすべてのデフォルト引数に適用されます)extern "C"
ブロックの内側ではありませんベースとメンバー
定義はテンプレートのためであれば
に同じコンストラクタ を呼び出す必要がありますODR-使用されているユニットは、すべてのこれらの要件は で定義し、依存名の時点で両方の名前に適用されますインスタンス化のポイント
これらの要件がすべて満たされている場合、プログラムは、 のように動作し、プログラム全体で1つの定義のみが存在します。それ以外の場合は、動作が定義されていません。
要するに、機能テンプレートが一部の翻訳単位でわずかに異なるものに展開されると、UBランドになります。あなたのプログラムは長い間働くかもしれないし、最適化のようなコンパイルオプションを変更すると突然クラッシュするので、ODR違反をデバッグすることは最悪です。
特定のケースでは、タイプが完全であるかどうかを検出して、関数の定義を変更する必要があります。いくつかの場所では、完全なタイプのとがその関数をインスタンス化するので、その関数の複数の異なる定義に終わるでしょう。
マクロにも注意してください。一部の翻訳でのみマクロ定義が変更され、そのマクロをテンプレートまたはインライン関数で使用すると、関数は全く同じトークンではないため、ODRに違反します。
ここで私は他の回答も実際に有用であることを認めます。タイプが完全であるかどうかを検出することは、全く役に立たないわけではありません。私は自分のコードで使っています。私はstatic_assert
という素晴らしい診断を提供するために使っています。これはSTLのいくつかの実装でさえ(GCCのSTLのunique_ptr
デストラクタ)行います。
私はこれがうまくいくことを期待していましたが、悲しいことにそれはしません... https://ideone.com/nEGsZu – Curious
タイプが完了しているかどうかによって仕事をすることができますが、同じことに、またはODR違反があります。 –
あなたがあなたがタイプが不完全であるという特性をタイプすると評価するならば、あなたのすべての翻訳ユニットであなたの特性は同じ結果に終わらなければなりません。 –