1. 程式人生 > >C++模板類之理解編譯器的編譯模板過程

C++模板類之理解編譯器的編譯模板過程

//-------------test.h----------------//
template<class T>
class A
{
public:
void f(); //這裡只是個宣告
};
//---------------test.cpp-------------//
#include”test.h”
template<class T>
void A<T>::f() //模板的實現,但注意:不是具現
{
…//do something
}
//---------------main.cpp---------------//
#include”test.h”
int main()
{
A<int> a;
a. f(); //編譯器在這裡並不知道A<int>::f的定義,因為它不在test.h裡面
//於是編譯器只好寄希望於聯結器,希望它能夠在其他.obj裡面找到
//A<int>::f的實現體,在本例中就是test.obj,然而,後者中真有A<int>::f的
//二進位制程式碼嗎?NO!!!因為C++標準明確表示,當一個模板不被用到的時
//侯它就不該被具現出來,test.cpp中用到了A<int>::f了嗎?沒有!!所以實
//際上test.cpp編譯出來的test.obj檔案中關於A::f的一行二進位制程式碼也沒有
//於是聯結器就傻眼了,只好給出一個連線錯誤
//但是,如果在test.cpp中寫一個函式,其中呼叫A<int>::f,則編譯器會將其//具現出來,因為在這個點上[test.cpp中],編譯器知道模板的定義,所以能//夠具現化,於是,test.obj的符號匯出表中就有了A<int>::f這個符號的地
//址,於是聯結器就能夠完成任務。
}

    關鍵是:在分離式編譯的環境下,編譯器編譯某一個.cpp檔案時並不知道另一個.cpp檔案的存在,也不會去查詢[當遇到未決符號時它會寄希望於聯結器]。這種模式在沒有模板的情況下執行良好,但遇到模板時就傻眼了,因為模板僅在需要的時候才會具現化出來,所以,當編譯器只看到模板的宣告時,它不能具現化該模板,只能建立一個具有外部連線的符號並期待聯結器能夠將符號的地址決議出來。然而當實現該模板的.cpp檔案中沒有用到模板的具現體時,編譯器懶得去具現,所以,整個工程的.obj中就找不到一行模板具現體的二進位制程式碼,於是聯結器也黔

http://dev.csdn.net/develop/article/19/19587.shtm
C++模板程式碼的組織方式 ——包含模式(Inclusion Model) 選擇自 sam1111 的 Blog
關鍵字 Template Inclusion Model
出處 C++ Template: The Complete Guide

    說明:本文譯自《C++ Template: The Complete Guide》一書的第6章中的部分內容。最近看到C++論壇上常有關於模板的包含模式的帖子,聯想到自己初學模板時,也為類似的問題困惑過,因此翻譯此文,希望對初學者有所幫助。

    模板程式碼有幾種不同的組織方式,本文介紹其中最流行的一種方式:包含模式。

    大多數C/C++程式設計師向下面這樣組織他們的非模板程式碼:類和其他型別全部放在標頭檔案中,這些標頭檔案具有.hpp(或者.H, .h, .hh, .hxx)副檔名。對於全域性變數和(非內聯)函式,只有宣告放在標頭檔案中,而定義放在點C檔案中,這些檔案具有.cpp(或者.C, .c, .cc, .cxx)副檔名。這種組織方式工作的很好:它使得在程式設計時可以方便地訪問所需的型別定義,並且避免了來自連結器的“變數或函式重複定義”的錯誤。由於以上組織方式約定的影響,模板程式設計新手往往會犯一個同樣的錯誤。下面這一小段程式反映了這種錯誤。就像對待“普通程式碼”那樣,我們在標頭檔案中定義模板:

// basics/myfirst.hpp

#ifndef MYFIRST_HPP
#define MYFIRST_HPP

// declaration of template

template <typename T>

void print_typeof (T const&);

#endif // MYFIRST_HPP



print_typeof()聲明瞭一個簡單的輔助函式用來列印一些型別資訊。函式的定義放在點C檔案中:

// basics/myfirst.cpp

#include <iostream>

#include <typeinfo>

#include "myfirst.hpp"


// implementation/definition of template

template <typename T>
void print_typeof (T const& x)
{

std::cout << typeid(x).name() << std::endl;

}

    這個例子使用typeid操作符來列印一個字串,這個字串描述了傳入的引數的型別資訊。

    最後,我們在另外一個點C檔案中使用我們的模板,在這個檔案中模板宣告被#include:

// basics/myfirstmain.cpp

#include "myfirst.hpp"

// use of the template

int main()
{

double ice = 3.0;
print_typeof(ice); // call function template for type double

}


    大部分C++編譯器(Compiler)很可能會接受這個程式,沒有任何問題,但是連結器(Linker)大概會報告一個錯誤,指出缺少函式print_typeof()的定義。

    這個錯誤的原因在於,模板函式print_typeof()的定義還沒有被具現化(instantiate)。為了具現化一個模板,編譯器必須知道哪一個定義應該被具現化,以及使用什麼樣的模板引數來具現化。不幸的是,在前面的例子中,這兩組資訊存在於分開編譯的不同檔案中。因此,當我們的編譯器看到對print_typeof()的呼叫,但是沒有看到此函式為double型別具現化的定義時,它只是假設這樣的定義在別處提供,並且建立一個那個定義的引用(連結器使用此引用解析)。另一方面,當編譯器處理myfirst.cpp時,該檔案並沒有任何指示表明它必須為它所包含的特殊引數具現化模板定義。

    解決上面這個問題的通用解法是,採用與我們使用巨集或者行內函數相同的方法:我們將模板的定義包含進宣告模板的標頭檔案中。對於我們的例子,我們可以通過將#include "myfirst.cpp"新增到myfirst.hpp檔案尾部,或者在每一個使用我們的模板的點C檔案中包含myfirst.cpp檔案,來達到目的。當然,還有第三種方法,就是刪掉myfirst.cpp檔案,並重寫myfirst.hpp檔案,使它包含所有的模板宣告與定義:

// basics/myfirst2.hpp

#ifndef MYFIRST_HPP
#define MYFIRST_HPP

#include <iostream>
#include <typeinfo>


// declaration of template
template <typename T>
void print_typeof (T const&);

// implementation/definition of template
template <typename T>
void print_typeof (T const& x)
{

std::cout << typeid(x).name() << std::endl;

}

#endif // MYFIRST_HPP

    這種組織模板程式碼的方式就稱作包含模式。經過這樣的調整,你會發現我們的程式已經能夠正確編譯、連結、執行了。

    從這個方法中我們可以得到一些觀察結果。最值得注意的一點是,這個方法在相當程度上增加了包含myfirst.hpp的開銷。在這個例子中,這種開銷並不是由模板定義自身的尺寸引起的,而是由這樣一個事實引起的,即我們必須包含我們的模板用到的標頭檔案,在這個例子中是<iostream>和<typeinfo>。你會發現這最終導致了成千上萬行的程式碼,因為諸如<iostream>這樣的標頭檔案也包含了和我們類似的模板定義。

    這在實踐中確實是一個問題,因為它增加了編譯器在編譯一個實際程式時所需的時間。我們因此會在以後的章節中驗證其他一些可能的方法來解決這個問題。但無論如何,現實世界中的程式花一小時來編譯連結已經是快的了(我們曾經遇到過花費數天時間來從原始碼編譯的程式)。

    拋開編譯時間不談,我們強烈建議如果可能儘量按照包含模式組織模板程式碼。

    另一個觀察結果是,非內聯模板函式與行內函數和巨集的最重要的不同在於:它並不會在呼叫端展開。相反,當模板函式被具現化時,會產生此函式的一個新的拷貝。由於這是一個自動的過程,編譯器也許會在不同的檔案中產生兩個相同的拷貝,從而引起連結器報告一個錯誤。理論上,我們並不關心這一點:這是編譯器設計者應當關心的事情。實際上,大多數時候一切都運轉正常,我們根本就不用處理這種狀況。然而,對於那些需要建立自己的庫的大型專案,這個問題偶爾會顯現出來。

    最後,需要指出的是,在我們的例子中,應用於普通模板函式的方法同樣適用於模板類的成員函式和靜態資料成員,以及模板成員函式。

相關推薦

C++模板理解編譯器編譯模板過程

//-------------test.h----------------// template<class T> class A { public: void f(); //這裡只是個宣告 }; //---------------test.cpp-------------// #

C++模板(函式)編譯 多檔案編譯

在C++中,模版和普通的函式或類有很多不一樣的性質。前兩天寫了一個模版類,標頭檔案和實現檔案分開存放的。這就出問題了,老是說找不到實現。查了一些資料才知道,原因是這樣的,編譯器在例項化一個類時,需要知道該類的所有確定的資訊,如果是普通的類這是完全由標頭檔案(.h)中類的宣告

C#FtpFtpWebRoesponse意外報停不響應 [System.Net.Sockets]

學無止境,此路甚長。 最近做專案遇到一個問題,自己是做後臺的,但涉及到網路的機會有些少,在這方面也是剛剛起步,在這裡記錄一下自己的成長,以供日後回望。 問題描述:FtpWebRoesponse接收伺服器反饋的時候,一直不相應,其實是因為FtpWebRoesponse拿不到訊息,一直苦苦等待,

c++ String 深淺拷貝

一、string類的實現:存放字串的指標、建構函式、拷貝建構函式、解構函式、賦值運算子過載 1、無引數的建構函式  給陣列初始化成,分配一個char空間存放‘\0’ 2、帶引數的建構函式 分配一個和傳進來的引數多一的空間用來存放‘\0’,然後初始化時進行復制。 3

django 模板語言 simple_tag 自定義模板

    自定義函式 simple_tag a. app專案下建立templatetags目錄 b. 建立任意xxoo.py檔案 用做自定義py函式  c. 建立template物件 register 在函式或者類 前面 加上建立的物件裝飾器register

模板 get_visitor的理解,以及模板函式物件當函式使用

最近程式出現了crash 問題,追蹤程式碼的時候,發現了這個模板類,是因為其返回了 空指標導致的,然後就做了一些實驗 template <typename T> struct get_visitor{ typedef T* result_type; resu

C# CultureInfo 各國語言所對應的的區域性名稱

CultureInfo 類儲存區域性特定的資訊,如關聯的語言、子語言、國家/地區、日曆和區域性約定。此類還提供對 DateTimeFormatInfo、NumberFormatInfo、CompareInfo 和 TextInfo 的區域性特定例項的訪問。這些物件包含區域性特定操作(如大小寫、格

模板模板函式特例化,模板特例化

今天在看某c++框架原始碼時,發現模板類裡的部分函式可以特例化,感覺很神奇便嘗試了下,發現很多平時不會注意的c++細節及知識,寫下來分享給大家,以便大家遇到相似問題時可以少踩點坑。 模板類會出現連結問題,編譯不通過 如果模板類.h檔案和.cpp檔案

C#設計模式行為模式:模板方法模式

frame 應該 ocp 方式 src 代碼復用 操作 優缺點 sse 定義(Template Method) 定義一個操作中算法的框架,而將一些步驟延遲到子類中。使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。 啟示 組裝電腦一般包含三個部分,主機、顯示

C++學習路(47)---C++模板模板深入詳解

1、在c++的Template中很多地方都用到了typename與class這兩個關鍵字,而且有時候二者可以替換,那麼是不是這兩個關鍵字完全一樣呢? 事實上class用於定義類,在模板引入c++後,最初定義模板的方法為:template<class T>,這裡cl

C++vector模板

    vector 稱為容器模板類,是同一種類型的物件的集合,每個物件都有一個對應的整數索引值。vector 不是一種資料型別,而只是一個類模板,可用來定義任意多種資料型別。vector 型別的每

linux下C++引用模板成員,編譯出錯

C++引用模板類成員,編譯出錯,錯誤類似於-》missing 'typename' prior to dependent type name 'SeqList<Type>::referen

C++模板函式與模板

C++中的模板是為了適應不同資料型別的呼叫。關鍵詞有template class typename template <typename T>T  max(T a, T b){  return (a>b) ? a:b;}class和typename在模板中意

C++模板(Template) 容器map 及 物件副本 深/淺拷貝 等問題

例如:假若你沒有提供CFileAttribute::operator=(過載賦值操作符),那麼語句fileAttribute1 = fileAttribute2就相當於:memcpy(&fileAttribute1, &fileAttribute2, sizeof(CFileAttribut

C++標準模板庫(STL)queue初步

multi ron stl 一個 c++ 第一個 基本 集合 emp 1,STL裏有些什麽? 包括三個內容:容器、叠代器、算法。 2,容器有哪些? 有stack, vector, queue, deque, list, set, multise

06深入理解C指針---指針型和長度

特征 都是 負數 意義 參數類型 同時 print 相關 通過   該系列文章源於《深入理解C指針》的閱讀與理解,由於本人的見識和知識的欠缺可能有誤,還望大家批評指教。   如果考慮到程序的可移植性和跨平臺性時,指針長度就是一個問題,需要慎重處理。一般情況下,數據指針的長度

C++模板頭文件和實現文件分離

證明 about compile strong 驗證 title htm -c itl http://www.cnblogs.com/lvdongjie/p/4288373.html 如何實現C++模板類頭文件和實現文件分離,這個問題和編譯器有關。 引用<<

c++模板--型擦除

ucs odk http dmr ocs gyp www cst inf aq鍬從坦0si盅4叭vtjhttp://shequ.docin.com/vfvj36952 6準耐背贍62惶d爛卦zphttp://tushu.docin.com/sina_6364886876 時

C#設計模式十三模板方法模式(Template Method Pattern)【行為型】

並集 client 變化 args 集中 pac 爸爸 rim 自己 原文:C#設計模式之十三模板方法模式(Template Method Pattern)【行為型】一、引言 “結構型”的設計模式已經寫完了,從今天我們開始講“行為型”設計模式。現在我們開始講【行為型】設

學藝不精而慚愧--論C++模板的使用

今天 lease stat entire 網上 永遠 hat efi 這樣的 自己斷斷續續地使用C++也有一段時間了。有些時候產生了自滿的情緒。覺得自己對C++的語言