使用模板的對象文件如何鏈接在一起

[英]How object files with templates are linked together


Imagine we have three .h files:

假設我們有三個。h文件:

f.h:

f.h:

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

g.h:

g.h:

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

h.h:

h.h:

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

Now, we also have three .cpp files:

現在,我們還有三個。cpp文件:

f.cpp:

f.cpp:

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

g.cpp:

g.cpp:

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

h.cpp:

h.cpp:

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

Compiling them gives us f.o, g.o and h.o. Now let's throw in this main.cpp:

編譯它們得到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;
}

A-a-and let's do g++ main.cpp f.o g.o h.o. Now comes my actual surprise: Since those three .o files contain three different definitions for int Class<int>::id(int), I expect to get a linking error. However, what I get is a working a.out, which prints 1 2 3. And if I reorder .o files in the command, it will print 101 102 103.

我們來做g++ main。cpp f。o g。現在,我的實際驚奇是:因為這三個.o文件包含了int類的三個不同的定義 ::id(int),我希望得到一個鏈接錯誤。然而,我得到的是一個工作a。輸出1 2 3。如果我在命令中重新訂購。o文件,它將打印1010102103。

And now for the actual questions: How exactly does the linker perform linking in this case? How does it figures out what instantiation of Class<int> to keep and what to throw away? And why it does not complain about multiple definitions?

現在來看實際的問題:在這種情況下鏈接器是如何執行鏈接的?它是如何計算出類的實例化,以保存和丟棄什么?為什么它不抱怨多重定義呢?

nm utility gives following output for nm f.o g.o h.o:

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

Clearly, f.o and g.o both export symbol __ZN5ClassIiE2idEi, and h.o imports this symbol (capital letters mean external linkage). And it leads to no errors. Why?

顯然,f。o和g。輸出符號__ZN5ClassIiE2idEi和h。o輸入此符號(大寫字母表示外部連接)。它不會導致錯誤。為什么?

2 个解决方案

#1


3  

This problem is actually well known: You're violating the ODR. Due to the nature of templates the compiler and linker don't notice that. What happens is the following:

這個問題實際上是眾所周知的:您違反了ODR。由於模板的性質,編譯器和鏈接器不會注意到這一點。發生的情況如下:

The linker assumes that the ODR has NOT been violated and is therefor free to use ANY of the instantiations of the template (aka. all instantiations of the template with the same parameters lead to the exact same code being generated). This system makes sense in as long as you don't violate the ODR.

鏈接器假定ODR沒有被違反,因此可以自由地使用模板的任何實例化(aka)。具有相同參數的模板的所有實例化都會生成完全相同的代碼)。只要不違反ODR,這個系統就有意義。

Now in your case the linker chooses to use the first instantiation it gets, which leads to the result being dependent on the order of linking.

現在在您的例子中,鏈接器選擇使用它得到的第一個實例化,這導致結果依賴於鏈接的順序。

#2


0  

Sounds too obvious, but your class is using implicit inlining. That is, when ever you put the source code inside the class declaration, you are "hinting" that you want it inlined. So...

聽起來太明顯了,但是您的類使用的是隱式內聯。也就是說,當您將源代碼放入類聲明中時,您就是在“暗示”您希望它被內聯。所以…

The only things the linker will see are f(),g() and h().

鏈接器只會看到f()、g()和h()。

It might be interesting to retry your code with something like this, although it is still so simple, the compiler might simply inline it still (depending on your compiler).

用這樣的東西重試代碼可能會很有趣,盡管它仍然很簡單,但是編譯器可能仍然直接內聯它(取決於您的編譯器)。

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

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

注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:https://www.itdaan.com/blog/2013/01/22/729b33d69ceed4bda99a4cb441b80f4e.html



 
粤ICP备14056181号  © 2014-2020 ITdaan.com