C Templates 深入模板基礎

2021-05-25 18:59:36 字數 3286 閱讀 5391

引數化宣告:

存在3種模板引數:

1.型別引數

2.非型別引數

3.模板的模板引數

非型別引數:

非型別引數表示的是:在編譯期或鏈結期可以確定的常值,這些引數的型別必須是下面的一種:

非型別模板引數的宣告和變數的宣告很相似,但它們不能具有static、mutable等修飾符;只能具有const和volatile限定符。但如果這兩個限定符限定的如果是最外層的引數型別,編譯器將會忽略它們。

非型別模板引數只能是右值,它們不能被取址,也不能被賦值。

模板的模板引數:

對於模板的模板引數而言,它的引數名稱只能被自身其他引數的宣告使用:

template class list>

class node

預設模板實參:

只有類模板宣告才能具有預設模板實參,預設實參不能依賴於自身的引數,但可以依賴於前面的引數。

對於任乙個模板引數,只有在之後的模板引數都提供了預設實參的前提下,才能具有預設模板實參。

預設實參不能重複宣告。

模板實參:

可以通過下面幾種機制來確定模板實參:

sfinae原則:替換失敗並非錯誤原則

sfinae原則保護的只是:允許試圖建立無效的型別,但並不允許試圖計算無效的表示式,下面是個錯誤的例子:

templatevoid f(int (&)[24/(4 - i)]);

templatevoid f(int (&)[24/(4 + i)]);

int main()

型別實參:

模板的型別實參是一些用例指定模板型別實參的值,平時使用的大多數leiixng都可以被用作模板的型別實參,但兩種情況例外:

1.區域性類和區域性列舉不能作為模板的型別實參。

2.未命名的class型別或未命名的列舉型別不能作為模板的型別實參

儘管其他的型別都可以用作模板實參,但前提是該型別替換模板引數之後獲得的構造必須是有效的。

非型別實參:

必須是以下幾種中的一種:

當實參匹配「指標型別或者引用型別的引數」時,使用者定義的型別轉換和由派生類到基類的型別轉換,都是不會被考慮的;即使在其他的情況下,這些隱式型別轉換是有效的,但在這裡都是無效的。

下面是一些有效的非型別模板實參的例子:

code:

template

class c;  

c* c1; //整型

int a;  

c* c2;    //外部變數的位址

void f();  

void f(int);  

c* c3; //函式名稱:在這個例子中,過載解析會選擇f(int),f前面的&隱式省略了

class x ;  

c* c4; //靜態類成員是可取的變數名稱

c* c5; //指向成員的指標常量

template

void temp1_func();  

c >* c6;    //函式模板例項同時也是函式

有些常值不能作為有效的非型別實參,包括:

解決辦法如下:

template

class message;

extern char const hello = "hello world!";

message* hello_msg;

下面是一些錯誤的例子:

code:

template

class c;  

class base  base;  

class derived : public base  derived_obj;  

c* err1;   //錯誤,這裡不會考慮派生類到基類的型別轉換

c* err2;  //錯誤,域運算子後面的變數不會被看成變數

int a[10];  

c* err3;   //錯誤,單一陣列元素的位址是不可取的

實參的等價性:

當每個對應實參值都相等時,就稱這兩組模板實參是相等的:

mix* p1;

mix* p2;

友元:

如果要把類模板的例項宣告為其他類(或者類模板)的友元,該類模板在宣告的地方必須是可見的。

通過確認緊接在友元函式名稱後面的是一對尖括號,我們可以把函式模板的例項宣告為友元:

template

void combine(t1, t2);

class mixer ;

不能在友元宣告中定義乙個模板例項,最多只能定義乙個特化。

如果名稱後面沒有緊跟一對尖括號,那麼只有在下面兩種情況是合法的:

1.如果名稱不是受限的,那麼該名稱一定不是引用乙個模板例項。如果在友元宣告的地方,還看不到所匹配的非模板函式(即普通函式),那麼這個友元宣告就是函式的首次宣告,於是,該宣告可以是定義。

2.如果名稱是受限的,那麼該名稱必須引用乙個在此之前宣告的函式或者函式模板。在匹配過程中,匹配的函式要優先於匹配的函式模板,然而,這樣的友元宣告不能是定義。

例:code:

void multiply (void*);  //普通函式

template

void multiply(t);       //函式模板

class comrades    //定義了乙個新的函式::multiply(int),非受限函式名稱,不能引用函式例項

friend

void ::multiply(void*);  //引用上面的普通函式

friend

void ::multiply(int);    //引用乙個模板例項

friend

void ::multiply(double*);   //受限名稱可以具有一對尖括號,但模板再次必須是可見的

friend

void ::error()        //錯誤,受限的友元不能是乙個定義

};  

在模板內部定義的友元函式的型別定義中,必須包含類模板的模板引數,否則友元函式會被生成多次,違反了odr原則:

template

class creator

};友元模板:

只有在友元模板宣告的是乙個非受限的函式名稱,並且後面沒有緊跟尖括號的情況下,該友元模板宣告才能成為定義。

友元模板宣告的只是基本模板和基本模板的成員。當進行這些宣告之後,與該基本模板相對應的模板區域性特化和顯式特化都會被自動地看成友元。

code:

class manager   

static

int counter;  

};  

C Templates 模板實戰

包含模型 把模板的定義包含在宣告模板的標頭檔案裡面,即讓定義和宣告都位於同乙個標頭檔案中。如果不需要考慮建立期的時間問題,建議盡量使用包含模型來組織模板 非內聯函式模板在呼叫的位置並不會被擴充套件,而是當它們基於某種型別進行例項化之後,才產生乙份新的 基於該型別的 函式拷貝。顯式例項化 顯式例項化指...

c templates 函式模板

1 定義函式模板 cpp view plain copy template typename t inline t const max t const a,t const b 解釋 template表明了這是乙個函式模板,指定了模板引數區域,typename表明了後面的引數是乙個型別名,t是模板引數...

C Templates 模板中的名稱

名稱的分類 識別符號 運算子id 型別轉換函式id 模板id 非受限id 受限id 受限名稱 非受限名稱 依賴性名稱 非依賴性名稱 如果乙個名稱使用域解析運算子或者成員訪問運算子來顯式表明它所屬的作用域,就稱該名稱為受限名稱。如果乙個名稱依賴於模板引數,就稱為依賴性名稱。名稱查詢 受限名稱的名稱查詢...