程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 模板實參演繹,模板參演繹

模板實參演繹,模板參演繹

編輯:C++入門知識

模板實參演繹,模板參演繹


一、什麼是實參演繹

如果我們每次都必須顯式的指明模板替換參數類型,例如concat<std::string, int>(s, 3),那麼過程將會顯得非常繁瑣。 如果我們可以concat(s, 3)//之前必須聲明s是std::string類型,那麼將會向普通的函數調用一樣簡單, 事實上,C++是允許這樣的寫法,然後C++編譯器會根據實參(s和3)的類型推算出合適的替換類型。   如何演繹?
1 template<typename T>
2 T const& max(T const& a T const& b)
3 {
4     return a < b ? b : a;
5 }
6 
7 int g = max(1, 1.0);
上面的演繹會失敗,為什麼?因為“函數模板對應的參數化類型T”只有一種,但是“調用實參類型”(1和1.0的類型為int型和double型),會導致演繹矛盾,所以失敗。 另一種失敗的情況是替換的類型導致一個無效的構造。如下:
 1 template<typename T>
 2 typename T::ElementT at(T const& a, int i)
 3 {
 4     return a[i];
 5 }
 6 
 7 void f(int* p)
 8 {
 9     int g = at(p, 1);// 這裡會演繹失敗,因為T被演繹成T*,但是int*沒有一個叫做ElementT的替換類型。
10 }
二、靈活的替換規則    為了方便我們下面的說明,我們來約定 來自調用實參類型 的類型為匹配類型A 來自函數模板對應的參數化類型T 的類型為匹配類型P (1)如果被聲明的參數 是一個帶引用的聲明(T& 或 T const&),那麼匹配類型P(就是T 或 T const), A仍然是實參類型,不會忽略高層次的const和volatile限定符號。 (2)如果被聲明的參數 是一個非引用的聲明,P就是所聲明的參數類型(比如T*),A仍然是實參類型, 同時還會忽略高層次的const和volatile限定符號。 (3)如果被聲明的參數 是一個非引用的聲明,如果這個實參是數組或者是函數類型(函數指針),那麼還會發生decay轉換,轉換為對應的指針類型,同時還會忽略高層次的const和volatile限定符號。 (4)實參7不能傳遞給int&
template<typename T> void f(T); //P是 T

template<typename T>void g(T&); //P是T

double x[20];

int const seven = 7;

f(x); //T被演繹成double*,decay轉化
g(x);//T被演繹成double[20]
f(seven);//T被演繹成int,忽略了高層的const
g(seven);//T被演繹成int const,不會忽略
f(7);//T被演繹成int
g(7);//T被演繹成int,但是7不能傳遞給int&

(5)又一個坑:實參為字符串,那麼演繹的結果T應該是字符串數組,不是字符串指針char*

template<typename T>
T const& max(T const& a, T const& b);

max("Apple", "Pear");“Apple”的類型是char const[6], “Pear”的類型是char const[5];而且不存在數組到指針的decay轉型(因為演繹的參數是引用參數)。因此為了演繹成功,T就同時必須得是char[6]和char[5]。這顯然是不可能的,所以產生錯誤。

三、練手

template<typename T>
void f1(T*);

template<typename E, int N>
void f2(E(&)[N]);

template<typename T1, typename T2, typename T3>
void f3(T1(T2::*)(T3*));

class S{
public:
    void f(double*);
};

void g(int ***ppp)
{
    bool b[42];
    f1(ppp); //演繹T為int***
    f2(b);     //演繹E為bool, N為42
    f3(&S::f); //演繹T1為void, T2為S,T3為double
}
演繹上下文的過程:匹配從最頂層開始,然後不斷的遞歸各種組成元素。 然而有兩類構造不能用於演繹上下文。 (1)受限的類型名稱,Q<T>::X的類型名稱不能用來演繹模板參數T (2)除了非類型參數外,模板參數還包含其他的成分的非類型表達式,諸如S<I+1>,int(&)[sizeof(S<T>)]類型不能用來演繹I和T 為什麼?因為演繹過程並不唯一(甚至不一定有限的情況內演繹完畢)。
template<int N>
class X{
public:
    typedef int I;
    void f(int){}
};

現在如果用 一個int 演繹typename X<N>::I ,那麼是不成功的,為什麼因為我可以有很多X<1>::i,X<2>::I, X<3>::I …… X<1000>::I,表示,所以C++禁止這種演繹。

template<int N>
void fppm(void (X<N>::*p)(typename X<N>::I));

int main()
{
    fppm(&X<33>::f);//可以演繹成功,N=33
}
在函數模板fppm()中,子構造X<N>::I不是一個可以演繹的上下文,但是可以通過成員指針類型(X<N>::*p)的成員部分X<N>演繹上下文。 四、特殊情況的演繹 存在兩種情況,其中演繹實參-參數對(A,P)並不是分別來自函數調用的實參,函數模板參數。
template<typename T>
void f(T, T);

void (*pf)(char, char) = &f;
(1)如上,第一種情況是取函數模板的地址的時候, A就是void(char, char), P就是void(T, T), T被演繹成char,同時pf被初始化為“特化f<char>“的地址 ------------------------我是華麗的分割線╮( ̄▽ ̄")╭------------------------------------
class S{
public:
    template<typename T, int N> operator T[N]&(); //我是轉型運算符,轉換成T[N]類型
};

void f(int(&)[20]); //參數的類型是20個元的數組

void g(S s)
{
    f(s);
}
我們試圖把S轉換成int(&)[20];因此類型A是int(&)[20], 類型P為T[N],於是用int替換T,用20替換N。演繹類型就是成功的。   五、再談靈活的替換規則 (1)對於模板參數-實參對(P, A),有兩種情況下,P可以比A多一個const或volatile限定符。 ①原來聲明的參數是一個引用參數子,那麼P類型可以比A類型多出一個const或volatile ②原來的聲明參數是不是一個引用參數子沒關系,A類型是指針或成員指針類型,那麼P類型也可以比A類型多出一個const或volatile。 (2)當演繹過程不涉及轉型運算符模板時,被替換的P類型可以是A類型的基類,或者是當A是指針類型時,P可以是一個指針類型,P指向的類型是A所指向類型的基類, 只有在不精確匹配情況下才會出現這中寬松匹配。
template<typename T>
class B{};

template<typename T>
class D:public B<T>{
};

template<typename T>
void f(B<T>*);

void g(D<long> dl)
{
    f(&dl); //成功,用long替換T
}
六、警告:類模板參數不能用於實參演繹   七、警告:函數模板的缺省實參不能用於實參演繹,即使實參不是依賴型的實參
template<typename T>
void f(T x = 42)
{}

int main()
{
   f<int>();  //正確,實例化
   f();//錯誤,不能用於實參演繹
}
八、一個小技巧 Barton-Nackman方法 當時這種方法被創建出來基於以下幾個原因: (1)當時函數模板不能被重載 (2)運算符==如果重載在類模板裡面那麼,根據上面的那些靈活的轉換方式(指向基類,指向子類之雲雲),第一個實參(this指針指向),第二個實參的轉型規則可能不一樣。 現在定義一個模板類Object,那如果要定義這個類的operator ==,那麼這個operator==不能定義在類內部(根據(2)), 也不能定義在全局或類之外的命名空間,如template<typename T> bool operator ==(Array<T> const& a, Array<T> const& b){……},(根據(1))   Barton和Nackman將這個運算符作為類的普通友元函數定義在類的內部。如下
#include <iostream>
using namespace std;

template<typename T>
class Object{
    public:
    int a;
    Object(int n):a(n){}
    friend bool operator == (Object<T> const&lt, Object<T> const& rt)
    {
        return equal(lt, rt);    //根據參數類型調用重載的函數
    }
};

bool equal(Object<int> const& lt, Object<int> const& rt) //這是一個普通函數,可以被隨便重載
{
    return lt.a == rt.a;
}

int main()
{
    Object<int> s(1);
    Object<int> s1(1);
    cout << (s == s1) << endl;
    return 0;
}

最後順利編譯通過,運行成功。

注:這些代碼的運行環境都是在mingw下進行的,VS2013估計自己重新實現了模板名字查找,很多書上說名稱找不到的情況,VS2013都找得到(-__-)b,所以為了更好的學習《C++Templates》轉投MinGW,編輯器是codeblocks。   編輯整理:Claruarius,轉載請注明出處。

C++模板實參的省略

第一種情況:template<typename T> void fun(T const& a,T const& b);但是你調用時卻是fun(250,250.4);那你就必須寫成fun<int>(250,250.4);
第二種情況:template<typename T,typename RT> RT fun(T const& a,T const& b);此時沒有辦法進行演繹,所以你可以重寫template<typename RT,typename T> RT fun(T const& a,T const& b);調用時寫成fun<double>(12,13);

第四種就是template<typename T> void fun();調用時直接是fun()!!
 

C++模板實參的省略問題

template 《class T》
函數返回類型 函數名( )
其中尖括號裡的是模板形參,下面圓括號裡的是模板函數的實參
給你個網址你看看wenku.baidu.com/...0.html
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved