Thinking in C++ 第二卷第五章的內容,先從模板的特化說起。模板的內容太豐富了,學習是一回事,要掌握模板肯定要在工作中經常使用。
模板特化我們知道,模板描述了一族類或者一族函數。實例化一個模板類,就是在這一族類中拿出特定的一種。特化就是萬中取一的過程。
顯式特化
自己提供代碼,而不是在實例化的時候特化。
例如:
template<class T, class Allocator = allocator<T> >
class vector{};
template<> class vector<bool, allocator<bool> > {..};
template<>明確告訴編譯器這是一個模板的特化。
在特化一個類模板的時候,一般要實現其中的所有成員函數。由于所提供的時一個單獨的類,客戶代碼常常希望能提供完整的接口實現。
半特化(偏特化)
沒有完全特化成特定的類型,特化的不完全體,還保留其他可能性。例:
template<class Allocator>class vector<bool, Allocator>{};
用戶可以提供一個自定義的allocator。
選擇哪個類模板來進行實例化的規則遵循“特化程度最高”的原則(半有序)。例:
template<class T, class U>
class X
{
public:
void f(){ cout << " Primary Template" << endl;}
};
template< class U>
class X<int, U>
{
public:
void f(){ cout << " T==int" << endl;}
};
template< class U>
class X<int, U>
{
public:
void f(){ cout << " T==int" << endl;}
};
template<class T>
class X<T,T>
{
public:
void f(){ cout << "T==U" << endl;}
};
int main()
{
C<float, int>().f();// Primary tempalte
C<int, float>().f();// T==int
C<int*, float*>().f();// T* and U* used , T = int, U = float
C<int, int>().f();//Duplicate X<int,U>, X<T>
return 0;
}
即,在特化或者偏特化的時候不要讓編譯器為難,盡量避免二義性的出現。
ps:如果類參數符合偏特化,就調用偏特化, 如果符合幾個偏特化,出現二義性,編譯錯誤
防止代碼膨脹
一旦對某個類模板進行了實例化, 伴隨著所有在程序中調用的該模板的成員函數,類定義中用于對其進行詳盡描述的特化代碼也會產生。只有被調用的成員函數才產生代碼。例:
class X
{
public:
void f(){}
};
class Y
{
public:
void g(){}
};
template<class T> class Z
{
T t;
public:
void a(){ t.f();}
void b(){ t.g();}
};
int main()
{
Z<X> zx;
zx.a();//Doesn't create Z<X>::b()
Z<Y> zy;
zy.b();//Doesn't create Z<Y>::a()
return 0;
}
但是雖然我們在代碼中只寫了一份,但是當你要將該類模板實例化為int型,void型和其他類型的時候,你會發現,編譯器在后臺為你復制粘貼了一系列相應的代碼,這使我們的代碼膨脹。解決方法是用void*進行完全特化,然后從void* 實現中派生出所有其他的指針類型。
template<
class T>
class Stack
{
T* data;
public:
void push(
const T& t){

}
void pop() {

}
};
template<>
class Stack<
void*>
{
void** data;
public:
void push(
const void*
const& t){

}
void pop(){

};
};
template<
class T> |
class Stack<T*> :
private Stack<
void*> |
{ |
typedef Stack<
void*> Base; |我理解是只產生這么一小段代碼,因為Base是已經特化好了的,所以Base的代碼只有定義一次。
public: |如果有個int*的實例的話,那就只要調用Base就行了,不必要再為int* 再生次一次代碼。
void push(T*
const& t) { Base::push(t);} |
void pop(){ Base::pop();} |
}; |
名字查找問題
當編譯器首次看到一個模板定義的時候,它不知道有關這個模板的任何信息,只有當它看到模板的實例化時,才能判斷這個模板是否被正確地使用了。 這種情況導致模板的編譯分兩個階段進行。
第一階段: 解析模板定義,尋找明顯的語法錯誤,解析所有能解析的符號。有些依賴與模板參數的符號就不能解析了。
第二階段: 編譯器決定是否用模板的一個顯式特化代替基本的模板。
123
posted on 2012-05-08 19:23
Dino-Tech 閱讀(252)
評論(0) 編輯 收藏 引用