C++模板详解
2014-09-09 01:48
239 查看
参考:C++ 模板详解(一)
模板:对类型进行参数化的工具;通常有两种形式:
函数模板:仅参数类型不同;
类模板: 仅数据成员和成员函数类型不同。
目的:让程序员编写与类型无关的代码。
注意:模板的声明或定义只能在全局、命名空间、类范围内进行。即不能在局部范围、函数内进行,比如不能在main函数中声明或定义一个模板。
template、class是关键字,class可以用关键字typename代替,在这里typename和class没区别。
<>中的参数叫模板形参,不能为空。模板形参用传入的实参类型来初始化。
模板形参可以在 返回类型、参数列表、函数体内使用。一旦编译器确定了模板实参类型,就称他实例化了函数模板的一个实例。
例1:swap函数的模板:
当调用这个函数模板时,类型T就会被被调用时的类型所代替。
比如swap(a,b),其中a和b是int 型,模板函数就变为swap(int &a, int &b)。
而当swap(c,d),其中c和d是double型时,模板函数会被替换为swap(double &a, double &b)。
例2:max函数的模板
即只能进行swap(2, 3); 或者 int a, b; swap(a,b); 这样的调用。
与函数模板一样,以template开始,后接模板形参列表,模板形参不能为空。
类的数据成员和函数成员可以使用模板形参。
例:一个类模板的例子:
两个模板形参:A<int, double> m; 类型之间用逗号隔开。
不能这样使用A<2> m; 类模板形参不存在实参推演的问题。
例:比如模板类A,有两个模板形参T1,T2,有一个成员函数 void func(),则外部定义该函数的语法为:
注意:当在类外面定义类的成员时,template后面的模板形参应与所定义的类的模板形参一致。
其中 T 就是一个类型形参,名字由用户确定。
函数模板,同一个类型形参,必须用相同类型的实参来初始化,比如
调用 func(2, 3.2); 将编译出错,因为模板形参T同时被指定为int 和 double,不一致,会出错。
类模板,其内部成员函数,则没有上述的限制,比如
声明 A<int> a; 调用 a.func(2, 3.2); 在编译时不会出错。
第二个实参3.2类型为double,在运行时,会强制类型转换为3。
例:模板类的对象调用成员函数:
非类型形参有几点要注意的:
在模板定义的内部是常量值,也就是说,上面的a在类B内部是一个常量。
形参只能是整型、指针、引用,像double、string、string **是不允许的,但是double &、double *、对象的引用或指针是正确的。
实参必须是一个常量表达式,即必须能在编译时计算出结果。注意:局部对象/变量和其地址,全局指针/变量/对象,都不是常量表达式;全局变量/对象地址或引用,const类型变量,sizeof的结果,都是常量表达式。
形参是整型时,实参也必须是整型的,且在编译期间是常量,比如
如果有int b; 这时 A<int, b> m; 将出错,因为b不是常量;如果有const int b; 这时 A<int, b> m; 正确,因为这时b是常量。
非类型形参一般不应用于函数模板中,比如有函数模板
若用func(2)调用,会出现无法为非类型形参a推演出参数的错误;可以用显示模板实参来解决,如用func<int, 3>(2); 把非类型形参a设置为整数3。
形参和实参间所允许的转换:
2 类模板,类型形参,默认值形式为:
3 类模板,类型形参,默认值和普通函数的默认参数一样,如果有多个类型形参,则从第一个设定了默认值的形参之后,所有模板形参都要设定默认值。
4 类模板,类型形参,外部定义类的成员函数。template 后的形参列表应省略掉默认值。
例:有默认值的类模板
模板:对类型进行参数化的工具;通常有两种形式:
函数模板:仅参数类型不同;
类模板: 仅数据成员和成员函数类型不同。
目的:让程序员编写与类型无关的代码。
注意:模板的声明或定义只能在全局、命名空间、类范围内进行。即不能在局部范围、函数内进行,比如不能在main函数中声明或定义一个模板。
一 函数模板
1 函数模板的格式:
template <class 类型名1,class 类型名2,......> 返回类型 函数名(参数列表) { 函数体 }
template、class是关键字,class可以用关键字typename代替,在这里typename和class没区别。
<>中的参数叫模板形参,不能为空。模板形参用传入的实参类型来初始化。
模板形参可以在 返回类型、参数列表、函数体内使用。一旦编译器确定了模板实参类型,就称他实例化了函数模板的一个实例。
例1:swap函数的模板:
template <class T> void swap(T& a, T& b) //参数列表使用模板形参 { T tmp = a; //函数体内使用模板形参 a = b; b = tmp; }
当调用这个函数模板时,类型T就会被被调用时的类型所代替。
比如swap(a,b),其中a和b是int 型,模板函数就变为swap(int &a, int &b)。
而当swap(c,d),其中c和d是double型时,模板函数会被替换为swap(double &a, double &b)。
例2:max函数的模板
#include<iostream> template<typename T> const T& myMax(const T &a, const T &b) { return a > b ? a : b; } int main() { cout << myMax(2.1, 2.2) << endl; //输出2.2 模板实参被隐式推演成double cout << myMax<double>(2.1, 2.2) << endl; //输出2.2 显示指定模板参数。 cout << myMax<int>(2.1, 2.2) << endl; //输出2 显示指定的模板参数,会将参数转换为int。 14 15 return 0; 16 }
2、注意:
不存在swap(int, int)这样的调用! 即不能用类型初始化,只能用实参推演来进行。即根据2来推出int型。即只能进行swap(2, 3); 或者 int a, b; swap(a,b); 这样的调用。
二 类模板
1 类模板的格式为:
template<class 形参名1, class 形参名2, ...> class 类名 { ... };
与函数模板一样,以template开始,后接模板形参列表,模板形参不能为空。
类的数据成员和函数成员可以使用模板形参。
例:一个类模板的例子:
template<class T> class A { public: T a; //类成员使用模板形参 T b; T func(T c, T &d); };
2 类模板对象的创建:
方法:A<int> m; 类A中用到模板形参的地方都会被int 所代替。两个模板形参:A<int, double> m; 类型之间用逗号隔开。
3 类模板形参必须指定类型而不是实参:
必须这样指定 A<int> m; 明确指定类型。不能这样使用A<2> m; 类模板形参不存在实参推演的问题。
4 在类模板外部定义成员函数的方法为:
template<模板形参列表> 函数返回类型 类名<模板形参名>::函数名(参数列表) { 函数体 }
例:比如模板类A,有两个模板形参T1,T2,有一个成员函数 void func(),则外部定义该函数的语法为:
template<class T1, class T2> //与类一致 void A<T1, T2>::func() //类名也要加上模板参数列表 { }
注意:当在类外面定义类的成员时,template后面的模板形参应与所定义的类的模板形参一致。
三 模板的形参
包括 类型形参、非类型形参、默认形参。1 类型形参
类型形参由关键字class或typename后接说明符构成,如template<class T> void func(T a) { };
其中 T 就是一个类型形参,名字由用户确定。
函数模板,同一个类型形参,必须用相同类型的实参来初始化,比如
template<class T> void func(T a, T b) { }
调用 func(2, 3.2); 将编译出错,因为模板形参T同时被指定为int 和 double,不一致,会出错。
类模板,其内部成员函数,则没有上述的限制,比如
template<class T> class A { public: T func(T a, T b); //或者T func(const T &a, const T &b); 普通引用会编译报错 };
声明 A<int> a; 调用 a.func(2, 3.2); 在编译时不会出错。
第二个实参3.2类型为double,在运行时,会强制类型转换为3。
例:模板类的对象调用成员函数:
#include <iostream> using namespace std; template<class T> class A { public: A(); T func(T a, T b); }; template<class T> //类外定义构造函数 A<T>::A() { } template<class T> //类外定义成员函数 T A<T>::func(T a, T b) { return a + b; } int main(int argc, char *argv[]) { A<int> ia; //模板实参为int类型的对象 cout << ia.func(3, 2.1) << endl; //输出5 A<double> da; cout << da.func(3, 2.1) << endl; //输出5.1 return 0; }
2 非类型形参
也就是内置类型形参,如template<class T, int a> //int a 就是非类型形参 class B { };
非类型形参有几点要注意的:
在模板定义的内部是常量值,也就是说,上面的a在类B内部是一个常量。
形参只能是整型、指针、引用,像double、string、string **是不允许的,但是double &、double *、对象的引用或指针是正确的。
实参必须是一个常量表达式,即必须能在编译时计算出结果。注意:局部对象/变量和其地址,全局指针/变量/对象,都不是常量表达式;全局变量/对象地址或引用,const类型变量,sizeof的结果,都是常量表达式。
形参是整型时,实参也必须是整型的,且在编译期间是常量,比如
template <class T, int a> class A { };
如果有int b; 这时 A<int, b> m; 将出错,因为b不是常量;如果有const int b; 这时 A<int, b> m; 正确,因为这时b是常量。
非类型形参一般不应用于函数模板中,比如有函数模板
template<class T, int a> void func(T b) { }
若用func(2)调用,会出现无法为非类型形参a推演出参数的错误;可以用显示模板实参来解决,如用func<int, 3>(2); 把非类型形参a设置为整数3。
形参和实参间所允许的转换:
//1 数组到指针,函数到指针的转换 template<int *a> class A { }; int b[10]; A<b> m; //数组转换成指针 //2 const修饰符的转换 template<const int *a> class A { }; int b; A<&b> m; //从int*转换成const int * //3 提升转换 template<int a> class A { }; const short b = 2; A<b> m; //short到int提升 //4 整数转换 template<unsigned int a> class A { }; A<3> m; //int到unsigned int转换 //5 常规转换
例:由用户指定栈的大小,并实现栈的相关操作。
#include <iostream> #include <string> #include <stdexcept> //std::out_of_range #include <cstdlib> //EXIT_FAILURE using namespace std; /*********模板类,声明开始,一般都是放在头文件的*********/ template<class T, int MAXSIZE> class myStack { public: myStack(); void push(T const &); //入栈 void pop(); //出栈 T top() const; //返回栈顶 bool empty() const //判断是否为空 { return size == 0; } bool full() const //判断栈是否已满 { return size == MAXSIZE; } private: T elems[MAXSIZE]; //使用数组存放栈元素,由于非类型形参MAXSIZE在类内是一个常量,所以可以用来声明数组大小 int size; //栈已使用空间 }; /**********模板类,声明结束,定义成员函数开始********/ template<class T, int MAXSIZE> myStack<T, MAXSIZE>::myStack(): size(0) //构造函数,初始化为空 { } template<class T, int MAXSIZE> void myStack<T, MAXSIZE>::push(T const &new_elem) //入栈 { if(size == MAXSIZE) { throw out_of_range("myStack::push(): stack is full"); } elems[size++] = new_elem; } template<class T, int MAXSIZE> void myStack<T, MAXSIZE>::pop() //栈顶出栈 { if(size <= 0) { throw out_of_range("myStack::pop(): stack is empty"); } --size; } template<class T, int MAXSIZE> T myStack<T, MAXSIZE>::top() const //返回栈顶元素 { if(size <= 0) { throw out_of_range("myStack::top(): stack is empty"); } return elems[size - 1]; } /***********成员函数定义结束**********************/ int main(int argc, char *argv[]) { try { myStack<int, 20> int20Stack; //显示模板实参 myStack<int, 40> int40Stack; myStack<string, 40> stringStack; int20Stack.push(7); cout << int20Stack.top() << endl; //输出7 int20Stack.pop(); for(int i = 0; i < 40; ++i) int40Stack.push(i); cout << int40Stack.top() << endl; //输出39 //int40Stack.push(41); //继续添加元素,会抛出异常,输出Exception: myStack::push(): stack is full stringStack.push("hello"); cout << stringStack.top() << endl; //输出hello stringStack.pop(); //stringStack.pop(); //继续出栈,会抛出异常,输出Exception: myStack::push(): stack is empty return 0; } catch(out_of_range const &ex) { cerr << "Exception: " << ex.what() << endl; return EXIT_FAILURE; } }
3 默认形参
1 类模板可以有默认值,函数模板不能有默认值。2 类模板,类型形参,默认值形式为:
template<class T1, class T2 = int> //为第二个模板类型形参提供int型的默认值 class A { };
3 类模板,类型形参,默认值和普通函数的默认参数一样,如果有多个类型形参,则从第一个设定了默认值的形参之后,所有模板形参都要设定默认值。
template<class T1 = int, class T2> //错误!如果T1有默认值,T2也必须有 class A { };
4 类模板,类型形参,外部定义类的成员函数。template 后的形参列表应省略掉默认值。
template<class T1, class T2 = int> class A { public: void func(); }; template<class T1,class T2> //定义方法,省略掉默认值 void A<T1,T2>::func() { }
例:有默认值的类模板
#include <iostream> using namespace std; template<typename T1, typename T2 = double, int abc = 5> //第二个类型形参和非类型形参有默认值 class A { public: void print(T1 a, T2 b); }; template<typename T1, typename T2, int abc> //类外定义时不能有默认值,毕竟类的声明是作为接口给别人看的 void A<T1, T2, abc>::print(T1 a, T2 b) { cout << a << ' ' << b << endl; cout << abc << endl; } int main(int argc, char *argv[]) { A<int> a; a.print(2.2, 2.1); //输出 2 2.1 5, 输出2是因为指定为int型,进行了类型转换,输出5是默认值 return 0; }
相关文章推荐
- C++模板详解
- C++模板的声明与实现分离 编译错误详解
- C++ - 函数模板定制(function template specialization) 详解 及 代码
- C++ 模板详解(一)
- C++模板详解
- C++的模板详解
- C++ 模板详解(一)
- C++ - 函数模板(function template) 的 重载(overload) 详解 及 代码
- C++ - 成员函数(member function)模板(template) 详解 及 代码
- C++中模板使用详解
- C++ 模板详解(一)
- C++模板详解(一)
- C++ 模板详解(二)
- C++ - 函数模板(function template) 的 重载(overload) 详解 及 代码
- C++ - 函数模板(function template) 详解
- C++模板声明定义分离编译错误详解
- C++ 模板详解(一)
- C++中的函数模板详解
- C++ 模板详解
- C++模板之特化与偏特化详解