次の 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.o
、g.o
両方とも export symbol__ZN5ClassIiE2idEi
とh.o
import this symbol (大文字は外部リンケージを意味します)。そして、それはエラーにつながりません。なんで?