如果說上一篇博文《模板名稱》是教人怎麼寫模板,那麼這一篇就是教人怎麼使用模板。
模板實例化的復雜性在於:對於產生自模板的實體,它們的定義已經不再局限於源代碼中的單一位置。 一、理解兩個概念 (1)實例化:實例化在C++中通常指“根據類型創建一個對象”,但是在模板裡面,實例化是指使用具體值替換模板實參,從模板中產生普通類,函數或者成員函數的過程。 (2)特化:這個過程最後獲得的試題就是我們所說的特化。 然而,實例化過程並不是特化的唯一方式,還有顯式特化,通過引入一個template<>來實現,如下: template<typename T1, typename T2> class MyClass{}; template<> class MyClass<std::string, float>{}; 二、按需實例化(on-demand實例化) 如果(某個組件)期望知道模板特化的大小,或者訪問該特化的成員,那麼整個定義就需要位於作用域中。 比如顯示的調用模板的成員,或者是包含隱式轉換 (1)顯示調用成員函數 template<typename T> class C; //前置聲明 C<int>* p = 0; //這裡只需要聲明就夠了 template<typename T> class C{ public: void f(); }; void g(C<int>& c){ c.f(); //此處需要知道整個模板的定義,因為編譯器要確定f()是不是可以被訪問到 } (2)隱式類型轉換 C++重載規則要求:如果候選函數的參數是class類型,那麼該類型所對應的類就必須可見 template <typename T> class C{ public: C(int); //單參數隱式類型轉換 }; void candidate(C<double> const&); //①允許編譯器實例化該重載函數,但不是必須的,在VS2013中,就沒有實例化參數 void candidate(int){} //② int main() { candidate(42); //編譯器不會選擇①處的聲明,因為一個精確的匹配要優於顯式轉型所獲得的匹配 return 0; } 三、延遲實例化 編譯器只對確實需要的部分實例化。換句話說,編譯器會延遲模板的實例化。 (1)當隱式實例化類模板時,同時也實例化了該模板的每一個成員函數的聲明,但並沒有實例化相應的定義。 但是有些情況是不會延遲的,如下: ①類模板裡面包含有匿名的union,那麼,匿名的union成員同時也被實例化, ②虛函數,作為實例化類模板的結果,許多編譯器實現都會實例化虛函數的定義,因為“實現虛函數調用機制的內部結構”要求虛函數的定義作為鏈接實體的存在。 (2)實例化類模板與實例化缺省的函數調用實參是分開的。換句話說,只有函數確實使用了缺省的實參,才會實例化該實參,如果這個函數不使用缺省的實參,那麼就不會實例化該缺省的實參,而是顯式使用實參來實例化。 template<typename T> class Safe{}; template<int N> class Danger{ typedef char Block[N]; //如果N<=0的話,將會出錯 }; template<typename T, int N> class Tricky{ public:virtual ~Tricky(){} //虛函數,並提供了定義};
void no_body_here(Safe<T>=3); //該缺省實參是可疑的,但沒有被使用,不會被實例化,不會出錯
void inclass(){
Danger<N>no_boom_yet; //沒有被使用,不會被實例化,不會出錯 } //void error(){ Danger<0> boom;} //如果沒有被注釋,會被要求給出這個類Danger<0>的完整定義,
//而實例化Danger<0>會出錯,即使沒有被使用,也不會被實例化,但仍然能夠引發一個錯誤
//該錯誤是在泛模板處理中產生的
//void unsafe(T(*p)[N]); //如果 沒有注釋掉的話,此處實例化聲明的時候會出錯 T operator->(); //virtual Safe<T> suspect(); //虛函數,但是沒有提供定義,所以會引發一個鏈接期的錯誤, //如果不注釋掉的話,鏈接器就會給出這類錯誤 struct Nested{
Danger<N> pfew; //因為沒有使用該結構,所以此處的沒有實例化
union{
int align;
Safe<T> anonymous;
}; }; int main() { Tricky<int, 0> ok; } 三、C++實例化模型 (1)兩階段查找 第一階段:使用普通查找規則(在適當情況也會使用ADL)對模板進行解析,查找非依賴型名稱。另外非受限的依賴型名稱(諸如函數調用中的函數名稱,因為其具有一個依賴型實參)也會在這個階段查找,只不過查找不完全,在實例化模板的時候還會再次進行查找。 第二階段:發生在模板被實例化的時候,我們也稱此時發生的地點(或源代碼的某個位置)為一個實例化點POI。依賴型受限名稱就在此時查找。另外,非受限的依賴型名稱在此階段也會再次執行ADL查找. (2)POI(實例化點) ①對於指向非類型(也就是函數╮( ̄▽ ̄")╭)特化的引用, C++把他的POI定義在“包含這個引用的定義或聲明之後的最近名字空間域”。 class MyInt{ public: MyInt(int i); }; MyInt operator - (MyInt const); bool operator >(MyInt const&, MyInt const&); typedef MyInt int; //② template <typename T> void f(T i) { if(i>0){ g(-i); //① } } //(1) void g(Int) //這就是那個定義或聲明 { //(2) f<Int>(42); //這就是那個引用 //(3) } //(4)這就是那個“之後最近的名字空間域”,函數f<int>的一個特化會出現在這裡 【注意】①位置的名稱g, 是非受限依賴型名稱,因為他的參數是依賴型的, 所以會在第二階段查找只是使用ADL就能夠找到函數g(Int)其實也就是g(MyInt);如果將MyInt替換成int,即②處為typedef int Int,那麼第二階段的查找關聯命名空間就會是空集,也就找不到函數g(Int)的聲明和定義。 ②對於產生自模板的類實例的引用,它的POI只能定義在“包含這個實例引用的定義或聲明之前的最近名字空間域”。 template<typename T> class S{ public: T m; }; //(5) 這裡就是S的POI unsigned long h() //這就是那個定義或聲明 { //(6) return (unsigned long) sizeof(S<int>); //這就是那個實例引用 //(7) } //(8) 四、顯式實例化 為模板特化顯式的生成POI是可行的,我們把這種特化的構造成為顯式實例化指示符。從語法關鍵字上講,它有關鍵字template和後面的特化聲明組成,所聲明的特化就是即將有實例化獲得的特化。 template<typename T> void f(T) throw(T){} 下面有4個有效的顯式實例化實體 template void f<int>(int) throw(int); template void f<>(int) throw(int); template void f(int) throw(int); //通過演繹獲得 template void f(int); //異常規范也可以省略,如果沒有省略,異常規范必須匹配相應的模板 C++規定: 同一個程序中,每一個特定的模板特化最多只能存在一處顯式實例化。而且,如果摸個模板特化已經被顯式實例化(使用template),那麼就不能對其進行顯式特殊化(使用template<>)。學編程 請上安全防線 百度第一
#include<iostream>
using namespace std;
template<class T> //這裡沒加這個模版
class stack
{
public:
stack(int MaxStackSize=10);
~stack(void){delete []element;}
bool IsEmpty()const {return top==-1;}
bool IsFull()const{return top==MaxTop;}
stack<T>& Add(const T &x);
stack<T>& Delete(T& x);
private:
int top;
int MaxTop;
T* element ;
};
class Position
{
public:
Position(void){};
~Position(void){};
int row,col;
};
template<class T>
stack<T>::stack(int MaxStackSize)
{
MaxTop=MaxStackSize-1;
element=new T[MaxStackSize];
top=-1;
}
template<class T>
stack<T>& stack<T>::Add(const T& x)
{
if(IsFull())
{
cout<<"棧滿"<<endl;
exit(1);
}
else
{
element[++top]=x; //這裡是element而不是stack
}
return *this;
}
template<class T>
stack<T>& stack<T>::Delete(T &x)
{
if(IsEmpty())
{
cout<<"棧空"<<endl;
}
else
{
x=element[top--];
}
return *this;
}
bool FindPath()
{
int **maze,m;
cout<<"請輸入矩陣的大小:"<<endl;
cin>>m;
stack<Position>*path;
path=new stack<Position>(m*m-1);
Position offset[4];
offset[0].row=0;offset[0].col =1;//向右
offset[1].row=1;offset[1].col =0;//向下
offset[2].row=0;offset[2].col =-1;//向左
offset[3].row=-1;offset[3].col =0;//向上
maze=new int *[m];
for(int i=0;i<m+2;i++)
{
cout<<"請輸入第"<<i+1<<"行元素:"<<endl;......余下全文>>