C++模板编程-模板基础重点
模板基础
1、模板参数自动推导,如果是已知的参数类型与个数,这调用模板时可以不写类型。
Cout<<max<int>(1,3);可以写为Cout<<max(1,3);这样看起来与普通函数没有区别。
2、最新标准C++11允许使用参数模板默认值,指定默认值后,如下
1 template < typename T0 = float, 2 typename T1, 3 typename T2 = float, 4 typename T3, 5 typename T4> 6 T2 func(T1 v1,T3 v3,T4 v4); 7 8 9 template < typename T0, 10 typename T1, 11 typename T2, 12 typename T3, 13 typename T4> 14 T2 func(T1 v1,T3 v3,T4 v4) 15 { 16 T0 static sv0 = T0(0); 17 T2 static sv2 = T2(0); 18 19 cout<<"\t v1: "<< v1 20 <<"\t v2: "<< v3 21 <<"\t v4: "<< v4 22 <<"\t || sv0: "<< sv0; 23 24 T2 v2 = sv2; 25 26 sv0 -= 1; 27 sv2 -= 1; 28 return v2; 29 }
3、函数模板中的静态局部变量
静态局部变量只有在调用同一个函数时才会操作静态变量,如果参数个数或者类型不同,则不会调用同一个模板函数实例,那局部静态变量也不会改变。
4、函数模板的声明和定义应该放在一个头文件里,这样引用时就不会出现各种问题。但是这样会出现重复引用问题。
5、针对上面出现的重复问题,C++解决方法是:在链接时识别及合并等价的模板实例。这里还要用到命名空间,对于自己写的库,一定要包括到自己的命名空间里,这样就不怕重复问题了。
6、类亦模板,与函数模板相同,但不支持模板实参推导机制,类模板如下
template <typename T = int>//此处可以指定默认值
struct list_node
{
T value;
list_node *next;
};
类模板使用起来和函数模板类似
list_node<int>int_node;
list_node<float>float_node;
如果指定默认值的话,也可以省去类型,但是“<>”不能省略。
list_node<>int_node;
7、C++规定: 在一个类模板内部出现的自身模板名,等价于该模板被调用时所生成的实例。
8、类模板同样可以用于派生生成子模板。
常用的类模板用法:
1 成员函数模板,2 友元函数模板,3 类模板的静态成员
类模板的静态成员,如果有两个文件都生成了含有静态成员变量的类模板的实例,那么在编译时会分配两个静态变量的内存地址,但是在链接时编译器会随机选择一个静态变量的内存地址作为最终的存储空间,从而使不同目标文件中的多个等价模板实例共享一套静态承运存储空间。
9、模板参数类型(typename or class指定的值)
整数模板参数,函数指针模板参数,指针及引用模板参数(局部变量指针或者引用不能用于模板参数),成员函数指针模板参数,模板型模板参数(模板的参数是另一个类模板)。
1 //成员函数指针模板参数 2 3 #include <iostream> 4 using namespace std; 5 6 class some_value; 7 typedef int (some_value::* some_value_mfp )(int);//some_value_mfp是指向一类函数的指针 8 9 template <some_value_mfp func>//func 是一个成员函数指针型模板参数 10 int call(some_value & value,int op) 11 { 12 return (value.*func)(op);//“.*”操作符和“->*”操作符会在这个情况下使用 13 } 14 15 class some_value 16 { 17 int value; 18 public: 19 some_value(int _value) {value = _value;} 20 int add_by(int op) {return value += op;} 21 int sub_by(int op) { return value -= op;} 22 int mul_by(int op) {return value *= op;} 23 }; 24 25 int main() 26 { 27 some_value v0(0); 28 cout<<call<&some_value::add_by>(v0,1)<<endl; 29 cout<<call<&some_value::sub_by>(v0,2)<<endl; 30 cout<<call<&some_value::mul_by>(v0,3)<<endl; 31 getchar(); 32 return 0; 33 }
1 //模板型模板参数 2 3 #include <iostream> 4 using namespace std; 5 6 //func是一个模板型模板函数,包装foreach要对没一个元素进的操作 7 template<template<typename TT> class Func,typename T>//只有类模板才能做为模板参数 8 void foreach(T array[],unsigned size) 9 { 10 Func<T> func; 11 for (unsigned i = 0 ; i < size ; i ++) 12 { 13 func(array[i]); 14 } 15 } 16 //三种操作都封装成函数类模板,可以通过括号操作符进行调用 17 template <typename T> 18 struct inc 19 { 20 void operator ()(T &v)const 21 { 22 ++v; 23 } 24 }; 25 template <typename T> 26 struct decs 27 { 28 void operator ()(T &v)const 29 { 30 --v; 31 } 32 }; 33 template <typename T> 34 struct print 35 { 36 void operator ()(T &v)const 37 { 38 cout<<' '<<v; 39 } 40 }; 41 42 43 int main() 44 { 45 int array[] = {1,2,3,4,5,6,7,8}; 46 foreach<print>(array,7); 47 cout<<endl; 48 49 foreach<inc>(array,7); 50 foreach<print>(array,7); 51 cout<<endl; 52 53 foreach<decs>(array,7); 54 foreach<print>(array,7); 55 getchar(); 56 return 0; 57 }
10、模板特例
模板的一种特殊情况。如果按集合划分的话,那就模板特例就是模板的一个真子集。
例如vector<bool> 模板特例,因为sizeof(bool) 返回一个字节,所以在申请空间时得到的就是一个字节(8bit)来保存一个bool型变量(1bit),有点浪费,所以有必要对其进行压缩处理,这是模板特例的一种方式。
1 //正常模板声明 2 template <typename T> 3 class my_vector 4 { 5 public: 6 //constructor use malloc to apply memory 7 my_vector(unsigned _size):array((T *)malloc(sizeof(T)*_size)),size(0),block_size(_size) {} 8 ~my_vector(){if(array) free(array);} 9 10 void push_back(T const &elem) throw (runtime_error) 11 { 12 if(size == block_size) 13 { 14 //已有空间已经用完,需要申请更大空间 15 block_size *= 2; 16 T * new_array = (T*)realloc(array,block_size* sizeof(T)); 17 if(new_array != NULL) 18 array = new_array; 19 else //申请增加空间失败,内存耗尽 20 { 21 free(array); 22 array = NULL; 23 throw runtime_error("out of memory"); 24 } 25 } 26 array[size++] = elem; 27 } 28 T operator[](unsigned i) {return array[i];} 29 const T &operator[](unsigned i) const {return array[i];} 30 //get how much memory we used 31 unsigned get_mem_size() const {return block_size * sizeof(T);} 32 protected: 33 private: 34 T *array; 35 unsigned size; 36 unsigned block_size; 37 38 }; 39 40 41 //模板特例声明,必须在正常模板声明之后 42 template<> 43 class my_vector<bool> 44 { 45 private: 46 int *array; 47 unsigned size; 48 unsigned block_size; 49 50 //一个段(segment)即一个整数值,段大小即为一个整数所能容纳的最多布尔值数 = sizeof(int)*8 51 const static unsigned seg_size; 52 53 public: 54 my_vector(unsigned _size):array((int*)malloc(_size*sizeof(int))),size(0),block_size(_size){} 55 ~my_vector() { if(array) free(array);} 56 57 void push_back(bool elem) throw (runtime_error) 58 { 59 if (size == block_size*seg_size) 60 { 61 block_size *= 2; 62 int *new_array = (int*)realloc(array,block_size*sizeof(int)); 63 if (new_array != NULL) 64 array = new_array; 65 else 66 { 67 free(array); 68 array = NULL; 69 throw runtime_error("out of memory"); 70 } 71 } 72 set(size++,elem); 73 } 74 75 76 void set(unsigned i , bool elem) 77 { 78 if (elem) 79 { 80 array[i/seg_size] |= (0x01 << (i % seg_size)); 81 } 82 else 83 { 84 array[i/seg_size] &= ~(0x01 <<(i%seg_size)); 85 } 86 } 87 88 bool operator[] (unsigned i) const 89 { 90 return (array[i/seg_size] &(0x01 << (i%seg_size))) != 0; 91 } 92 93 unsigned get_mem_size() const {return block_size*sizeof(int);} 94 }; 95 96 const unsigned my_vector<bool>::seg_size = sizeof(int) * 8; 97 98 99 int main() 100 { 101 my_vector<char> vi(2); 102 my_vector<bool> vb(2); 103 for (unsigned i = 0 ; i < 20; i ++) 104 { 105 vi.push_back('a'+ i); 106 vb.push_back((i%4) == 0); 107 } 108 109 cout<<"char "<<vi.get_mem_size()<<endl; 110 cout<<"bool "<<vb.get_mem_size()<<endl; 111 112 113 for (unsigned i = 0 ; i < 20 ; i ++) 114 { 115 cout<<' '<<vi[i]; 116 } 117 cout<<endl; 118 119 for (unsigned i = 0 ; i < 20 ; i ++) 120 { 121 cout<<' '<<vb[i]; 122 } 123 cout<<endl; 124 125 getchar(); 126 return -1; 127 }
结果:
总结:针对某一问题,我们可以首先为绝大多数情况设计模板。然后针对集中特殊情况设计模板特例,从而尽量用最小的代码量得到尽量最优化的解决方法。
11、C++规定:同一个模板可以有多个特例。
使用时匹配原则是:匹配最特殊的那个。
比较两个特例A和B,如果所有能匹配A的模板参数值都能匹配B,而反之不成立的话,那就确定A比B特殊。即A是B的真子集。
1 #include <iostream> 2 using namespace std; 3 4 5 template <typename T0,typename T1, typename T2> 6 struct S 7 { 8 string id() { return "General";} 9 }; 10 //第三个参数必须为char 11 template <typename T0,typename T1> 12 struct S<T0,T1,char> 13 { 14 string id() {return "Specialization #1";} 15 }; 16 //第二第三个参数必须为char 17 template <typename T0> 18 struct S<T0,char,char> 19 { 20 string id(){return "Specialization #2";} 21 }; 22 //第一个参数为int,且第二第三个参数相同 23 template <typename T> 24 struct S<int,T,T> 25 { 26 string id() {return "Specialization #3";} 27 };
12、函数模板的特例与重载
C++只允许为函数模板声明完全特例,而禁止声明部分特例。需要用到部分特例的情况可以用函数模板重载来实现。
1 //函数模板特例与重载 2 3 #include <iostream> 4 using namespace std; 5 6 template <typename T> 7 void print(T v) 8 { 9 cout<<v<<endl; 10 } 11 12 //定义成模板特例(完全特例) 13 template <> 14 void print<char>(char v) 15 { 16 cout<<'\''<<v<<'\''<<endl; 17 } 18 19 //模板特例,模板参数依赖推导(完全特例) 20 template<>// const char *v有函数实参可以推导出模板实参的类型,故可以省略template<> 21 void print(const char *v) 22 { 23 cout<<'"'<<v<<'"'<<endl; 24 } 25 26 //函数重载 27 inline void print(string const &v) 28 { 29 cout<<"\'\'\'"<<v<<"\'\'\'"<<endl; 30 } 31 32 inline void print(bool v) 33 { 34 cout<<(v?"true":"false")<<endl; 35 } 36 37 //函数模板重载 38 template <typename T> 39 void print(T *v) 40 { 41 cout<<'*'; 42 print(*v); 43 }
13、分辨重载
普通函数重载和函数模板重载后调用就会有多个匹配函数,这是就要做选择。
C++准则:
1、 两个候选函数中如果一方其形参列表类型与调用实参列表各类型更匹配,则淘汰另一方。
2、 两函数如果其形参列表类型同等匹配实参列表类型,若一方为函数模板实例而另一方为非模板函数,则取非模板函数而淘汰函数模板实例。
3、 两函数如果其形参列表类型同等匹配实参列表类型时,若两者均为函数模板实例,则取更加特殊的一方而淘汰另一方。
14、编译期的条件判断逻辑
模板特例实际上提供了一种判断逻辑—当模板参数满足某一匹配条件时所匹配特例实现,否则用通例实现。不过这种判断是在编译器实现的。所输入的值必须事先知道并且确定。
例子是:不使用循环及条件判断语句打印1~100之间的数字。
1 //编译期的条件判断逻辑 2 #include <iostream> 3 using namespace std; 4 5 //通用模板实例 6 template <int i > 7 void print() 8 { 9 print<i-1>(); 10 cout<<i <<endl; 11 } 12 13 //模板特例 14 template <> 15 void print<1>() 16 { 17 cout<<1<<endl; 18 } 19 20 21 int main() 22 { 23 print<100>(); 24 25 getchar(); 26 return 0; 27 }
本文来自博客园,作者:struggle_time,转载请注明原文链接:https://www.cnblogs.com/songliquan/p/4094718.html