假如我们有以下函数
int compare(const string &v1,const string &v2)
{
if(v1<v2)return -1;
if(v1>v2)return 1;
return 0;
}
这个函数比较两个值并指出两个值的大于,小于或者等于关系。
1.函数模板
1.
template<typename T>
int compare(const T&v1,const T&v2)
{
if(v1<v2)return -1;
if(v1>v2)return 1;
return 0;
}
2.使用函数模板
int main()
{
//T是int
cout<<compare(1,0)<<endl;
//T是string
string s1="hi",s2="world";
cout<<compare(s1,s2)<<endl;
return 0;
}
3.inline函数模板
inline关键字放在template之后,函数类型之前
//正确写法
template<typename T>inline T min(const T&,const T&)
//错误写法
inline template<typename T>T min(const T&,const T&)
2.类模板
1.定义
template<class Type>class Queue{
public:
Queue();
Type &front();
const Type &front()const;
void push(const Type &);
void pop();
bool empty()const
private:
...
};
2.使用类模板
与使用函数模板形成对比(1.2),使用类模板时,必须为模板形参显式指定实参
queue<int>qi;
Queue<vector<double>>qc;
Queue<sting>qs;
模板特化
有些时候,我们编写的模板定义并不总是适用于所有类型
template<typename T>
int compare(const T&v1,const T&v2)
{
if(v1<v2)return -1;
if(v1>v2)return 1;
return 0;
}
当这个函数的参数类型是C风格字符串时,这个函数不能正常工作(这个函数将比较两个指针在内存中的相对位置,但没有比较指针指向对象的大小),这时候我们必须提供一个针对C风格字符串的特殊定义,这就是模板特化
1.函数模板的特化
特化的形式如下:
- 关键字tempalte后面接一对空的尖括号(< >)
- 函数名后接模板名和一对尖括号,尖括号中指定这个特化定义的模板形参
- 函数形参表
- 函数体
template<>
int compare<const char *>(const char *const &v1,const char *const &v2)
{
return strcmp(v1,v2);
}
1.声明模板特化
与任意函数一样,函数模板特化可以声明而无须定义。
template<>
int compare<const char *>(const char *const &,const char *const &);
2.函数重载与模板特化
如果在特化中省略空的模板形参表template<>,那么结果是函数的重载而不是模板特化
int compare(const char *const&,const char *const&);
//声明了该函数的重载版本而不是模板版本
3.特化的作用域规则
在调用模板的这个特化版本之前,特化的声明必须在作用域中
template<class T>
int compare(const T&t1,const T& t2)
{
...
}
int main()
{
int i=compare("hello","world");
...
}
template<>
int compare<const char *>(const char *const &s1,const char *const &s2)
{
...
}
这个程序有错误,这个函数将会调用未特化的原模板函数
特化出现在对该模板实例的调用之后是错误的
2.类模板的特化
1.类特化的定义
template<> class Queue<const char*>{
public:
void push(const char *);
void pop();{real_queue.pop();}
bool empty()const{return real_queue.empty();}
std::string front(){return real_queue.front();}
const std::string &front()const{return real_queue.front();}
private:
Queue<std::string>real_queue;
};
类模板特化应该与它所特化的模板定义相同的接口,否则当用户试图使用未定义的成员时会感到奇怪
2.在类特化外部定义成员
void Queue<const char *>::push(const char*val)
{
return real_queue.push(val);
}
在类特化外部定义成员时,成员之前不能加template<>标记
3.特化成员而不特化类
template<>
void Queue<const char*>::push(const char *const &val)
{
...
}
template<>
void Queue<const char *>::pop()
{
...
}
现在,类类型Queue<const char *>将从通用类模板定义实例化而来,而push和pop函数例外。调用Queue<const char *>对象的push或pop函数时,将调用特化版本;调用其他任意成员时,将从类模板为const char*实例化一个通用版本。
4.类模板的部分特化
如果类模板有一个以上的模板形参,我们也许想要特化某些模板形参而非全部,使用类模板的部分特化可以做到这一点:
template<class T1,class T2>
class some_template{
//...
};
//部分特化
template <class T1>
class some_template<T1,int>{
//...
}
使用类模板的部分特化
some_template<int,string>foo;//使用类模板
some_template<string,int>bar;//使用部分特化
1.foo的实例化类型与提供的部分特化类型不匹配,因此,foo的类型必然从通用类模板实例化
2.bar选择部分特化模板来实例化。当声明了部分特化的时候,编译器将为实例化选择最特化的模板定义,当没有部分特化可以使用的时候,就使用通用模板定义。
ps:函数模板是没有部分特化的,如果声明了一个部分特化的函数模板,编译器会报错