2つのソースファイルの同じ名前のクラスに含まれるクラス定義を決定するものは何ですか?を参照してください。、 1つの定義規則の意図的で明確な違反がありますが、コンパイラー/リンカーが1つの定義を別の定義から選択するオプションを持つことがどのように可能であるかについてはまだ混乱しています。
(回答/コメントに基づく補遺:意図的に標準に違反しているため、コードが未定義の動作をもたらす場合、コンパイラー/リンカーが以下に示す結果を生成する方法の1つの例を探しています。)
コードサンプルは次のとおりです。
// file1.cpp:
#include <iostream>
#include "file2.h"
struct A
{
A() : a(1) {}
int a;
};
int main()
{
// foo() <-- uncomment this line to draw in file2.cpp's use of class A
A a; // <-- Which version of class A is chosen by the linker?
std::cout << a.a << std::endl; // <-- Is "1" or "2" output?
}
..。
//file2.h:
void foo();
..。
// file2.cpp:
#include <iostream>
#include "file2.h"
struct A
{
A() : a(2) {}
int a;
};
void foo()
{
A a; // <-- Which version of class A is chosen by the linker?
std::cout << a.a << std::endl; // <-- Is "1" or "2" output?
}
この場合、関数foo()
は1を出力することもあれば、2を出力することもあります。
しかし、のコンストラクターA
はインラインです!関数呼び出しではありません!したがって、コンパイラには、関数のコンパイル時a
に関数自体のコンパイル済みコード内にオブジェクトをインスタンス化するコードのアセンブリ/マシン命令を含める必要があると思います。foo()
foo()
したがって、後で、リンク時に、リンカーは、コンパイルされたバイナリfoo()
に関数を含めることを決定したときの定義について、アセンブリ/マシン命令を変更しないと思います(実際には呼び出されていることがわかっているだけなので) 、リンク時)。この理由によると、リンカーはどのインラインコンストラクターコードが関数にコンパイルされるかに影響を与える可能性がないため、One Definition Ruleの意図的な違反にもかかわらず、常に使用されるインラインコンストラクターのfile2バージョンである必要があります。foo()
foo()
foo()
のコンストラクターA
がインラインでない場合、関数foo()
がコンパイルされるときに、関数へのJUMPステートメント(のコンストラクターA
)が関数のアセンブルされたコード内に配置される可能性があることを理解しますfoo()
。その後、リンカ時に、リンカはJUMPステートメントのアドレスに、のコンストラクタの2つの定義を選択して入力できA
ます。
実際には、インラインコンストラクターが存在するにもかかわらず、印刷する場合とfoo()
印刷する場合があるという事実について私が考えることができる唯一の説明は、コンパイラーが「file2.cpp」をコンパイルするときに、コンパイルされたアセンブリ/マシンコードでスペースを作成することです。 Aのコンストラクターへのインライン呼び出しの関数ですが、実際にはアセンブリ/マシンコード自体を入力しません。その後、リンケージ時に、リンカーは、コンストラクターのインライン関数の2つの定義の間の(任意の)選択を使用して、関数自体のコンパイル済み定義内の事前に決定された場所にコンストラクターのコードをコピーします。 。1
foo()
2
foo()
A
foo()
A
私の説明は正しいですか、それとも別の説明がありますか?A
この例では、1つの定義規則に故意に違反しているにもかかわらず、コンストラクターの呼び出しがインラインである場合に、コンパイラー/リンカーがどのコンストラクターを呼び出すかを選択できるようにするにはどうすればよいでしょうか。
補遺:コメントと回答に応じて、タイトルを変更し、上部に説明の段落を追加して、この例では動作が定義されていないことを理解し、方法の1つの例を探していることを明確にしました実際のコンパイラ/リンカは、観察された動作を一度でも生成する可能性があります。特定の時点での動作を予測する答えを探しているわけではないことに注意してください。
補遺2:コメントに応えてA a;
、VSデバッガーの行にブレークポイントを設定し、「逆アセンブリ」ビューを選択しました。実際、逆アセンブルコードから、「インライン」の存在にもかかわらず、この場合、コンパイラーはオブジェクトのコンストラクター呼び出しをインライン化しないことを選択したことは明らかですa
。
したがって、Alfの答えは正しいです。コンストラクターの暗黙inline
にもかかわらず、コンストラクター呼び出しはインライン化されていません。
したがって、接線の問題が発生します。コンストラクターが通常のメンバー関数よりもインライン化される可能性が低いかどうかについて、明確なステートメントを作成できますか(inline
どちらの場合も、明示的または暗黙的に存在すると仮定します)。これについてステートメントを作成でき、答えが「はい、コンパイラーは通常のメンバー関数inline
を拒否するよりもコンストラクターを拒否する可能性が高いinline
」である場合、フォローアップの質問は「なぜ」でしょうか。