模板實例化

若是說上一篇博文《模板名稱》是教人怎麼寫模板,那麼這一篇就是教人怎麼使用模板。html

閱讀此文以前要先閱讀上一篇博文理解什麼事受限名稱,什麼是依賴型名稱 → 函數

http://www.cnblogs.com/claruarius/p/4063795.htmlspa

模板實例化的複雜性在於:對於產生自模板的實體,它們的定義已經再也不侷限於源代碼中的單一位置。
1、理解兩個概念
(1)實例化:實例化在C++中一般指「根據類型建立一個對象」,可是在模板裏面,實例化是指使用 具體值替換模板實參,從模板中 產生普通類,函數或者成員函數過程
(2)特化:這個過程最後得到的試題就是咱們所說的特化。
然而,實例化過程並非特化的惟一方式,還有顯式特化,經過引入一個template<>來實現,以下:
template<typename T1, typename T2>
class MyClass{};
 
template<>
class MyClass<std::string, float>{};
 
2、按需實例化(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;
 
3、延遲實例化
編譯器只對確實須要的部分實例化。換句話說,編譯器會延遲模板的實例化。
(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;      //由於沒有使用該結構,因此此處的沒有實例化htm

};
 

union{對象

 

    int align;blog

 

Safe<T> anonymous;作用域

 
};
};
 
int main()
{
    Tricky<int, 0> ok;
}
3、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;    //②
void e(){}//③
template <typename T>
void f(T i)   
{
    if(i>0){
        g(-i);   //①
         e(); //非受限非依賴型名稱會在第一階段查找,因此若是要順利解析須要在此模板以前(能夠在③位置)定義函數e(),不然解析不經過
        h(-i);//h是非受限依賴型名稱,因此函數h沒必要出如今模板f()以前,讓其可見(由於能夠經過ADL查找到名稱),以下。
}
}
//(1)
void g(Int)     //這就是那個定義或聲明
{
//(2)
    f<Int>(42);  //這就是那個引用
//(3)
}
//(4)這就是那個「以後最近的名字空間域」,函數f<int>的一個特化會出如今這裏
 
void h(Int)
{
    f<Int>(32);
}
 
【注意】①位置的名稱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) 
 
4、顯式實例化  
爲模板特化顯式的生成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<>)。
相關文章
相關標籤/搜索