《深刻實踐C++模板編程》之三——模板參數類型詳解

非類型模板參數 和 模板型模板參數
整數以及枚舉類型;指向對象或者函數的指針;對對象或函數的引用;指向對象成員的指針。統稱爲非類型模板參數。
模板型模板參數,是指模板參數還能夠是一個模板。
 
一、整數模板參數
非類型模板參數的做用至關於爲函數模板或類模板預約義一些常量,在生成模板實例時,也要求必須以常量即編譯期已知的值爲非類型模板參數賦值。 //就是模板中有一個參數,但它並非模板參數,並不會適配不一樣的類型,而是某種固定的類型 那麼他的好處在哪?「模板中聲明的常量,在模板的全部實例中都具備相同的值,而非類型模板參數則對於在不一樣的模板實例中擁有不一樣的值來知足不一樣的需求」。這句話說得太官方了,咱們來看個例子:
template<typename T>
class CArray { static cosnt unsigned size = 10; T elems[size]; public: T& operator[](unsigned i) throw (std::out_of_range) { if (i >= size) { throw std::out_of_range("Access out of range\n"); } else { return elems[i]; } } };

 

這個例子存在什麼問題?問題就在於數組的大小被寫死了,這個模板在編譯器只能靈活適配不一樣的數組類型,可是沒法適配不一樣的數組大小。可是若是改爲這樣,就會靈活不少:
 1 template<typename T, unsigned Size>
 2 class CArray2  3 {  4 T elems[Size];  5 public:  6 T& operator[](unsigned i) throw (std::out_of_range)  7 {  8 if (i >= size)  9 { 10 throw std::out_of_range("Access out of range\n"); 11 } 12 else
13 { 14 return elems[i]; 15 } 16 } 17 };

 

讓咱們來驗證一下,非類型模板參數Size的值不一樣,是否產生的是不一樣的函數實例,稍微改造一下函數以下:
 
template<typename T, unsigned Size>
class CArray2 { public: CArray2() { id++; } ~CArray2(){} T elems[Size]; public: T& operator[](unsigned i) throw (std::out_of_range) { if (i >= size) { throw std::out_of_range("Access out of range\n"); } else { return elems[i]; } } public: static int id; }; template<typename T, unsigned Size> int CArray2<T, Size>::id = 0; //順便咱們也應該瞭解這種帶有非類型模板參數的模板類如何定義一個static成員
 
void main() { CArray2<char, 20> array0; printf("ID:%d\n", array0.id); CArray2<char, 20> array1; printf("ID:%d\n", array1.id); CArray2<int, 10> array3; printf("ID:%d\n", array3.id); CArray2<int, 20> array4; printf("ID:%d\n", array4.id); getchar(); } 

 

運行結果以下:

 

 

 
二、函數指針模板參數
前面一小節,在模板參數中固定寫死了某種數據類型,這裏咱們也能夠在定義模板時固定寫死某種函數參數類型。而這個函數參數類型又能夠適配模板中的模板參數類型。
 
 1 template<typename T, void(*f)(T &v)>
 2 void foreach(T array[], unsigned size)  3 {  4 for (unsigned i = 0; i < size; ++i)  5 {  6 f(array[i]);  7 }  8 }  9  
10 template<typename T>
11 void inc(T &v){ ++v; } 12  
13 template<typename T>
14 void dec(T &v){ --v; } 15  
16 template<typename T>
17 void print(T &v){ printf("%d ", v); } 18  
19 void main() 20 { 21 int array[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; 22 foreach<int, print<int>>(array, 8); 23  
24 foreach<int, inc<int>>(array, 8); 25  
26 getchar(); 27 }
 
三、指針及引用模板參數
只有指向全局變量及外部變量及類靜態變量的指針及引用才能夠做爲模板參數。函數的局部變量、類成員變量等均不能做爲模板參數。由於模板參數值必須是編譯時已知的。
 
 1 template<int* p>
 2 struct wrapper  3 {  4 int get(){ return *p; }  5 void set(int v){ *p = v; }  6  
 7 };  8  
 9 template<int &p>
10 struct wrapper2 11 { 12 int get(){ return p; } 13 void set(int v){ p = v; } 14 }; 15  
16 int global_variable = 0; 17  
18 int main() 19 { 20 wrapper<&global_variable> gwrapper; 21 wrapper2<global_variable> gwrapper2; 22 }

 

有一個明確的結論是,global_variable 決定了gwrapper的類型。即若是我添加一個
int global_variable3 = 0; 和 wrapper<global_variable3> gwrapper3;
那麼gwrapper和gwrapper3並不是同一個類型。
以前咱們講述的是用typename T來區分兩個模板實例,可是這裏的一個指針、一個整形常量(統稱非模板型模板參數)就能夠直接區分模板實例:
 1 template<int* p>
 2 struct wrapper  3 {  4 public:  5 wrapper(){ id++; }  6 int get(){ return *p; }  7 void set(int v){ *p = v; }  8 public:  9 static int id; 10 }; 11 template<int* p> int wrapper<p>::id = 0; 12  
13 int global_variable = 0; 14 int global_variable3 = 0; 15  
16 int main() 17 { 18 wrapper<&global_variable> gwrapper; 19 printf("ID:%d\n", gwrapper.id); 20  
21 wrapper<&global_variable> gwrapper4; 22 printf("ID:%d\n", gwrapper4.id); 23  
24 wrapper<&global_variable3> gwrapper3; 25 printf("ID:%d\n", gwrapper3.id); 26  
27 getchar(); 28  
29 }

 

四、成員函數指針模板參數
 
class some_value { int value; public: some_value(int _value) :value(_value){} int add_by(int op){ return value += op; } int sub_by(int op){ return value -= op; } int mul_by(int op){ return value *= op; } }; typedef int (some_value::* some_value_mfp)(int); template<some_value_mfp func>
int call(some_value &value, int op){ return (value.*func)(op); }//*是必要的,不然會認爲是在使用value類的成員func
 
void main() { some_value v0(0); printf("%d\n", call<&some_value::add_by>(v0, 1));//&是必要的,不然會認爲是調用some_value::add_by可是沒給參數
printf("%d\n", call<&some_value::sub_by>(v0, 2)); printf("%d\n", call<&some_value::mul_by>(v0, 3)); getchar(); }
 
 
五、模板型模板參數
首先我有三個模板:
template<typename T>
struct inc { void operator()(T &v) const { ++v; } }; template<typename T>
struct dec { void operator()(T &v) const { --v; } }; template<typename T>
struct print { void operator()(T &v) const { std::cout << ' ' << v; } };
 
這三個模板決定了foreach生成不一樣的實例(固然還有foreach自己的第二個模板參數),這裏注意只有類模板能夠做爲模板參數,因此這裏只能用class而不能用struct:
template<template<typename TT> class Func, typename T>
void foreach(T array[], unsigned size) { Func<T> func; for (unsigned i = 0; i < size; i++) { func(array[i]); } }

 

在foreach中使用第一個模板 or 在foreach中使用第二個模板 or 在foreach中使用第三個模板?都有可能!因此要在foreach中添加一個模板參數用來決定使用哪一個模板,這就是模板的模板,也就是模板型模板參數。
void main() { int array[] = { 1, 2, 3, 4, 5, 6, 7 }; foreach<print>(array, 7); foreach<inc>(array, 7); foreach<dec>(array, 7); getchar(); }
相關文章
相關標籤/搜索