函数模,类模板

函数模板特化

函数模板特化:

  1. 关键字template后面接一对<>
  2. 再接模板名和一对<>,尖括号中定义这个特华的模板参数
  3. 函数形参表
  4. 函数体
template <typename T>
int com(const T &v1,const T &v2) 
{
    return v1>v2?1:0;
}

template <>
int com<const string>(const string &v1,const string &v2)
{
    return strcmp(v1.c_str(),v2.c_str());
}

特化的声明必须与对应的模板相匹配,当调用cmp函数时,传给他两个const string类型的参数,编译器调用特化版本,特化函数参数固定为const string类型,当调用其他类型(包括string)时,调用泛化版本

1.声明特化模板

函数特化模板可以声明而无需定义

template <>
int com<const string>(const string &v1,const string &v2)

注意:

  1. 模板特化总是包含模板空参说明符(template<>)
  2. 必须包含函数形参列表,如果可以从函数形参推断模板实参,不必显示定义模板实参
template <>
int com(const string &v1,const string &v2)
{
    return strcmp(v1.c_str(),v2.c_str());
}

2.函数重载与模板特化

如果省略template<>,则是函数的重载非模板版本

template <typename T>
int com(const T &v1,const T &v2) 
{
    return v1>v2?1:0;
}

//template <>
int com(const string &v1,const string &v2)
{
    return strcmp(v1.c_str(),v2.c_str());
}

注意:

  当定义非模板函数的时候,对实参应用常规转换;特化模板的时候,对应的实参类型不应用转换。

  模板特化版本的调用中,实参类型必须与特化版本的函数类型参数完全匹配,若果不完全匹配,编译器将为实参从模板定义实例化一个实例。

3.不能总是检测到重复定义

  应在一个头文件中包含模板特化的声明,每个源文件包含该头文件

完整代码

#include <iostream>
#include <cstring>
using namespace std;

template <typename T>
int com(const T &v1,const T &v2) 
{
    return v1>v2?1:0;
}

template <>
int com(const string &v1,const string &v2)
{
    return strcmp(v1.c_str(),v2.c_str());
}

int main()
{
    int a=1,b=2;
    string s1="123",s2="1";
    
    cout<<com(s1,s2)<<endl;
    return 0;
}

类模板特化

  特化可以定义与模板本身不同的成员。如果一个特化无法从模板定义某个成员,该特化类型就不能使用该成员。类模板的定义不会用于显示创建特化成员的定义。

1.类特化定义

  在类特化外部定义成员时,成员前不加template<>

/*
 *类特化为 const char*类型,上面的函数特化类似
 *此例子只是为了说明类特化模板定义成员 
 */

void queue<const char*>::push(const char *val) 
{
    return real_queue.push(val);
}

2.特化成员而不特化类

template<> 
void queue<const char*>::push(const char *val);

与任何特化函数模板一样,以空参的形参表开头,在定义类的头文件中

3.类模板的部分特化

template <class T1,class T2>
class some_templae
{
    //
}

//特化T2为int类型,T1为任何类型 
template<T1>
class some_template<T1,int>
{
    //
}

注意:

  1. 部分特化像类模板的定义
  2. 部分特化的模板形参是类模板定义的形参表子集
  3. 部分特化形参表只列出未知模板形参的那些实参
#include <iostream>
#include <cstring>
using namespace std;

template <class T1,class T2>
class Ship
{
    public:
        Ship(T1 a1,T2 a2):a(a1),b(a2){}
        void show()
        {
            cout<<a<<" "<<b<<endl;
        }
    private:
        T1 a;
        T2 b;
};

//特化T2为int类型,T1为任何类型 
template<class T1>
class Ship<int,T1>//<T1,int>都可以,int和T1的位置可以交换 
{
    public:
        Ship(T1 a1,int a2):a(a1),b(a2){}
        void show()
        {
            cout<<a<<" "<<b<<endl;
        }
    private:
        T1 a;
        int b;
};

int main()
{
    Ship<double,int> s(1.222,2);//调用通用版本,也可调用通用版本以为当生命部分特化时,编译器会选择最特化的版本,当无部分特化时,选择通用版本
    Ship<double,double> s2(6.66,9.99) ;//调用特化版本 
    
    s.show();
    s2.show();
    return 0;
}

注意:

  部分特化的定义与通用模板的定义不冲突,部分特化可以具有与通用模板完全不同的成员集合,类模板成员的通用定义永远不会用实例化模板部分的特化成员

重载与函数模板

(1)为该函数建立候选集合:

   a.与背调函数名相同的任意普通函数

   b.任意模板实例化,模板实参发现了与调用中所用函数参数相匹配的模板实参

(2)确定哪些普通函数的的行为是可行的,候选集合中的每个实例模板都可行

(3)如果需要转换来进行调用,根据转换的种类排列可行函数,模板函数实例转换有限

   a.如果只有一个函数可选,就调用他

   b.有多个可选,具有二义性,去掉所有模板实例

(4)重新排列去掉函数模板实例的可用函数

   a.有一个则调用

   b.否则具有二义性

  1. 如果只包含一个函数可选,就调用这个函数
  2. 否则具有二义性
//函数模板 
template<typename T
int com(const T&,const T&);

//普通函数 
int com(const char*,const char*);

char s1[]="sdfa",s2[]="dfaskf";
com(s1,s2);//会调用普通函数,因为函数会将数组转化为指针,调用普通函数优先于调用模板 

转换与重载函数模板

  当普通函数与模板函数都同样匹配,且一样好时,非模板版本有限

猜你喜欢

转载自www.cnblogs.com/tianzeng/p/9782207.html