9

次の 3 つの.hファイルがあるとします。

f.h:

template <typename T> class Class {public: Class() {} T id(T x) { return x; }};

g.h:

template <typename T> class Class {public: Class() {} T id(T x) { return x + 100; }};

h.h:

template <typename T> class Class {public: Class(); T id(T x); };

.cppこれで、次の 3 つのファイルもあります。

f.cpp:

#include "f.h"
int f(int x) { Class<int> t; return t.id(x); }

g.cpp:

#include "g.h"
int g(int x) { Class<int> t; return t.id(x);  }

h.cpp:

#include "h.h"
int h(int x) { Class<int> t; return t.id(x); }

それらをコンパイルするとf.o、 、g.oおよびが得られh.oます。これを投入しましょうmain.cpp:

#include <stdio>

extern int f(int);
extern int g(int);
extern int h(int);

int main() {
   std::cout << f(1) << std::endl;
   std::cout << g(2) << std::endl;
   std::cout << h(3) << std::endl;
}

あー、やりましょうg++ main.cpp f.o g.o h.o。これら 3 つの.oファイルには の 3 つの異なる定義が含まれint Class<int>::id(int)ているため、リンク エラーが発生することが予想されます。しかし、私が得たのはa.out、 を出力する作業中の1 2 3です。.oコマンドでファイルを並べ替えると、出力されます101 102 103

それでは、実際の質問です。この場合、リンカーはどのように正確にリンクを実行しますか? どのインスタンス化Class<int>を保持し、何を破棄するかをどのように判断するのでしょうか? また、複数の定義について文句を言わないのはなぜですか?

nmユーティリティは、次の出力を提供しますnm f.o g.o h.o

f.o:
00000000 b .bss
00000000 d .data
00000000 t .text
00000000 t .text$_ZN5ClassIiE2idEi
00000000 t .text$_ZN5ClassIiEC1Ev
00000000 T __Z1fi
00000000 T __ZN5ClassIiE2idEi
00000000 T __ZN5ClassIiEC1Ev

g.o:
00000000 b .bss
00000000 d .data
00000000 t .text
00000000 t .text$_ZN5ClassIiE2idEi
00000000 t .text$_ZN5ClassIiEC1Ev
00000000 T __Z1gi
00000000 T __ZN5ClassIiE2idEi
00000000 T __ZN5ClassIiEC1Ev

h.o:
00000000 b .bss
00000000 d .data
00000000 d .eh_frame
00000000 t .text
00000000 T __Z1hi
         U __ZN5ClassIiE2idEi
         U __ZN5ClassIiEC1Ev

明らかにf.og.o両方とも export symbol__ZN5ClassIiE2idEih.oimport this symbol (大文字は外部リンケージを意味します)。そして、それはエラーにつながりません。なんで?

4

2 に答える 2

3

この問題は実際にはよく知られています: ODR に違反しています。テンプレートの性質上、コンパイラとリンカーはそれを認識しません。何が起こるかは次のとおりです。

リンカーは、ODR に違反していないため、テンプレートのインスタンス化を自由に使用できると想定します (つまり、同じパラメーターを持つテンプレートのインスタンス化はすべて、まったく同じコードが生成されます)。このシステムは、ODR に違反しない限り意味があります。

あなたの場合、リンカーは取得した最初のインスタンス化を使用することを選択します。これにより、結果はリンクの順序に依存します。

于 2013-01-22T13:11:11.300 に答える
0

あまりにも明白に聞こえますが、クラスは暗黙的なインライン化を使用しています。つまり、ソース コードをクラス宣言内に配置するときはいつでも、それをインライン化することを "ほのめかしている" ことになります。そう...

リンカーが認識するのは、f()、g()、および h() だけです。

このようなコードを再試行するのは興味深いかもしれませんが、これはまだ非常に単純ですが、コンパイラーは単にインライン化する可能性があります (コンパイラーによって異なります)。

template <typename T> class Class 
{
public: 
     Class() {}
T id(T x);
};

template <typename T> 
T Class::id(T x)
{ 
   return x; 
}
于 2013-01-22T14:15:33.743 に答える