• 特化与偏特化


    函数模板特化

      模板参数在某种特定类型下的具体实现称为模板的特化

    函数模板特化:

    1. 关键字template后面接一对<>
    2. 再接模板名和一对<>,尖括号中定义这个特华的模板参数
    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());
    }

    注意:

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

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

     

    类模板特化

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

    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 partitial specialization)是模板特化的一种特殊情况,指显示指定部分模板参数而非全部模板参数,或者指定模板参数的一部分而非全部特性,也称为模板部分特化

    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);//会调用普通函数,因为函数会将数组转化为指针,调用普通函数优先于调用模板 

    转换与重载函数模板

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

  • 相关阅读:
    [NOIP-P1125]逃亡的准备
    [NOIP-P1125]两个数差
    问题 B: [NOIP-P1125]飙车
    [NOIP-P1125]计算概率
    牛跳
    化学方程式
    c++第十七章-(内联函数)
    c++第十六章-(函数模板与类模板)
    《将博客搬至CSDN》
    cocoaPods安装与使用
  • 原文地址:https://www.cnblogs.com/tianzeng/p/9782207.html
Copyright © 2020-2023  润新知