一 模板函数
模板不支持类型的自动转换,重载的时候非模板函数的优先级高于模板函数,例如
int max(int a, int b);
template <typename T> max(T a, T b);
那么
::max(3,5); //调用的是int max(int a, int b);,因为其优先级高
::max<>(3,5); //则显示调用模板函数template <typename T> max(T a, T b);
::max(3, 4.2); //调用int max(int a, int b);,因为非模板函数支持类型的自动转换,而模板函数不能
::max('a', 5); //调用int max(int a, int b);,因为非模板函数支持类型的自动转换
::max('a', ‘b’); //调用模板函数int max<char>(char a, char b);可以通过实参演绎
另外对于不能由调用参数来决定的模板函数调用,我们可以显示实例化,如template <typename T1, typename T2, typename RT> RT fun(T1 a, T2 b);我们只能传入两个参数,而返回类型RT是不知道的,这个时候我们就必须显示指定,如 fun<int, float, float>(1,2.2)。但这样做感觉有点繁琐,其实T1, T2是可以从传入参数中得知的,那我们改进一下模板函数,把RT放到最前面,即 template <typename RT, typename T1, typename T2> RT fun(T1 a, T2 b); 这样我就可以这样 fun<float>(1,2.2),看起来就简单多了。我们需要指定最后一个不能由调用参数隐身演绎的模板参数之前的所有参数类型,在这里不能被调用参数演绎的最后一个参数是RT,之前也没有了,所以指定RT就好了。
二 模板类
模板类的声明为
template <typename T>
class A{
T m_var;
......
};
如果其中要用到类A的变量,则用A<T>来表示,如果只是类名用A,如果构造函数
实例化一个类并生成一个对象之后,只有被调用到的成员函数才会产生函数的实例化代码,也就是被调用的时候才实例化,这样至少有两个好处:1)节省空间和时间;2)对于那些未能提供成员函数中所有操作的类型来说也可以用,只要不调用为支持的操作即可。
类模板的特化
特化就是用具体的类型来代替模板类型,并对类做一些特殊处理。如
tempelate <>
class a<int>{
...
};
为什么需要特化呢,不管归于函数模板还是类模板,我们写好之后,对于有些特定类型是不适用的,而为了能用这个类型,我们必须进行一些特殊处理,其实相当于重新定义了一个类或函数。这里我们一函数模板为例
template <typename T>
T max(T a, T b) { return (a>b ? a:b);}
那么如果我想要char *类型也适用的话,就要进行特化
template <>
char * max(char * a, char* b) { return (strcmp(a,b) > 0) ? a:b;}
注意:特化的模板必须放在非特化的模板的之后,类模板也是,要不然编译器要报错的。
我们可以局部特化,也可以全特化,但不能有二义性。
文章推荐 http://www.blogjava.net/bacoo/archive/2009/06/22/283480.html
类模板缺省参数
类模板可以有缺省参数,而且可以用之前的模板类型,如
template < typename T, typename CONT=std::vector<T> >
class stack{ ... };
这里std::vector<T> 作为CONT的默认参数,而且用了之前的模板参数T
三 非类型模板参数
模板参数可以是非类型的,如
template <typename T, int MAXSIZE>
class A{...};
这里的MAXSIZE并不是一个类型,而是一个int型的值,用来表示大小
但是非类型模板参数只能是整型或者是指向外部链接对象的指针(如字符串文本,“abcd”,但char [] aa ="abcd",aa是可以作为参数的;全局指针也不行)。 浮点型和类对象是不能作为模板参数的。参考 http://blog.csdn.net/sophia_sy/article/details/1507077
四 一些技巧
模板的成员和参数都可以是模板
typename的其他使用
template <typename T>
class A{
typename T::SubType *ptr;
};
这里SubType是T中的一种类型,如果前面不加typename,那么SubType就会被认为是T中的静态变量了。
对基类的成员访问
对于具有基类的类模板,x和this->x并不一定一下,如果要正确的访问基类的依赖于模板参数的成员x,最好用this->x,或Base<T>::x
template <typename T>
class Derived: public Base<T>{};
成员模板
模板中的成员也可以是模板,如嵌套类和成员函数。对于A<int> 和 A<float>之间的对象是不能赋值的,如果要赋值就需要重载=运算符,
template <typename T>
class A{
...
template <typename T2>
A<T>& operator = (const A<T2>&);
};
这里的operator = 就是一个模板函数
模板参数对象的零初始化
如果T是一个内建的类型,那么T x;如果没有进行初始化就是一个不确定的值,为了防止意外发生我们需要进行初始化。可以这样声明T x=T();那么即便T是一个内建类型如int,也会进行初始化,对于int是0,bool是false。
字符串作为模板实参需要注意的问题
template <typename T>
T& max(const T &t1, const T &t2);
max("abcd", "cdef"); //正确
max("abcd","hello"); //错误,因为"abcd"是const char[5], 而"hello"是const char[6]
如果max里面的参数不是引用的话,T max(const T t1, const T t2);则两者都正确,因为如果不是引用的话,在实参的演绎过程中,数组会转化为char *,这样类型就一致了。
五 模板的链接
模板的名称必须是唯一的,不能和类型定义的变量或struct等定义的变量冲突
如int x;
template <typename T> class x;
这样就会出现编译错误。
另外模板的链接只能是c++的,不能是c链接
如extern "c" template <typename T> void fun(); 将是错误的
六 模板实例化
以模板函数为例,如
template <typename T>
T max(T t1, T t2)
{
if(t1 > t2)
return t1;
else
return t2;
}
当我们用具体的类型替换T的时候,即调用max(3,5);的时候就会先实例化,生成一个类似于int max(int t1, int t2)这样的函数。在实例化的过程中编译器需要检查模板函数的定义,看其中的每一个操作是否合法,例如 类型是否支持 > , 如果不支持就会出错,而普通的函数只有知道函数的声明即可。这也就是为什么我们把模板函数的声明和定义分别放在.h .cpp中,只包含.h文件编译出错的原因。
对于模板类来说,模板类并不是和一般的类或类型类似的概念,不能直接定义对象;实例化之后产生的模板实例才是和普通类或类型类似的概念,可以定义对象。
例如
template <typename T>
class A{
T m_var;
......
};
A<>并不能定义对象,而A<int>是实例化之后的实例,可以定义对象。