STL 类模板的特化、类型形参缺省、数值形模板参数、模板技巧

1. 全局特化

1. 全类特化

  • 哪种类型不好使,就对哪种类型做特化
  • 对类模板做全类特化就相当于重新写了一个类,而且这个类是专门针对之前那个不好使的类型的
    在这里插入图片描述

2. 成员特化

哪个成员函数不好使就对哪个成员函数做特化
在这里插入图片描述

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


// 所以这个类中的add函数不适合 const char* 类型, 因为指针不能做加法
template<class T>
class CMath{
public:
    CMath(T const& t1, T const t2):m_t1(t1), m_t2(t2){}

    T add(void){
            return m_t1 + m_t2;
    }
private:
    T m_t1;
    T m_t2;
};

// 全类特化,相当于重新写一个针对特定类型(char* const)的类
/*
template<>
class CMath<char* const>{
public:
    CMath(char* const& t1, char* const& t2):m_t1(t1), m_t2(t2){}

    char* const add(void){
            return strcat(m_t1, m_t2);
    }
private:
    char* const m_t1;
    char* const m_t2;
};
*/

// 就比较麻烦,因为只有add函数不适合char* const类型, 只重写add函数就好
// 所有就有了成员特化
template<>
char* const CMath<char* const>::add(void){
        return strcat(m_t1, m_t2);
}

int main(void){
    CMath<int> m1(1, 1);
    std::cout << m1.add() << std::endl; // 2
    // 字符数组名是 char* const
    char c_x[256] = "hello ", c_y[256] = "world!";
    // 遇到 CMath<char* const>,就不会再去使用类模板实例化类,而是直接使用那个特化后的类
    CMath<char* const>m2(c_x, c_y);
    cout << m2.add() << endl;           // hello world!
    return 0;
}

2. 局部特化

函数模版不存在局部特化,只有类模版才能局部特化?
在这里插入图片描述

  • 和全局特化的差别就是,看特化了几个类型参数,如果把所有的类型参数都特化成具体类型就是全局特化,如果只特化一部分类型参数就是局部特化。
#include <iostream>
using namespace std;

// 类模板
template <class T, class D>
class A{
public:
    // 这里写static是为了可以直接通过类调用,不用实例化对象
    static void foo(){
        cout << "1. A<T, D>::foo()" << endl;
    }
};

// 局部特化, 把D特化成short
template <class T>
class A<T, short>{
public:
    static void foo(){
        cout << "2. A<T, short>::foo()" << endl;
    }
};

// 局部特化, 把D特化成T
template <class T>
class A<T, T>{
public:
    static void foo(){
        cout << "3. A<T, T>::foo()" << endl;
    }
};

int main(void){
    A<int, double>::foo();  // 1. A<T, D>::foo()
    A<int , short>::foo();  // 2. A<T, short>::foo()
    // A<short, short>::foo(); //编译报错,在2和3 之间产生歧义
    return 0;
}

3. 类型形参缺省

在这里插入图片描述

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

// 类模板
template <class T = short, class D = double>
class A{
public:
    void print(void){
        cout << "m_t: " << typeid(m_t).name() << ","
             << "m_d: " << typeid(m_d).name() << endl;
    }
private:
    T m_t;
    D m_d;
};

int main(void){
    A<int, float> m1;
    m1.print(); // m_t: i,m_d: f
    A<>m2;
    m2.print(); // m_t: s,m_d: d
}

4. 数值形的模板参数

在这里插入图片描述

  • 类型参数表中可以出现,数值型参数,还可以带缺省值
  • 不能出现字符串,double这种类型,只能出现数值型参数,只能出现 int类型?
  • size_t 是 long unsigned int
#include <iostream>
using namespace std;

// 类型参数表中可以出现,数值型参数,还可以带缺省值
template<class T, size_t S> // size_t S = 10
class Array{
public:
    // 操作符重载
    T& operator[](size_t i){
        return m_arr[i];
    }
    size_t size(){
        return S;
    }
private:
    T m_arr[S];
};

int main(void){
    // Array<int> a; a 可以看做一个一维数组类,数组中每一个元素就是int类型
    // m 可以看做一个一维数组,数组中每一个元素都是 Array<int> 类型,一个一维数组
    // 所以m是一个二维数组
    Array<Array<int,4>, 4 > m;   // 后面两个< 分开写,避免和<< 歧义
    for (int i=0; i<m.size(); i++){
        for (int j=0; j<m.size(); j++){
            m[i][j] = 1;
        }
    }
    for (int i=0; i<m.size(); i++){
        for (int j=0; j<m.size(); j++){
            cout << m[i][j] << " ";
        }
        cout << endl;
    }    
    return 0;
}


$ ./a.out 
1 1 1 1 
1 1 1 1 
1 1 1 1 
1 1 1 1 

5. 模板技巧

个人感觉后面这两个都不常见,不过既然看到了,就大概写一下,留一个种子,万一以后开发中碰到了,也便于扩展。

5.1 模板型成员变量

在这里插入图片描述

  • m_s 首先是一个成员变量,然后是由类模板 Arrary 实例化出的未知类,因为D是未知的
#include <iostream>
using namespace std;

template<class T>
class Array{
public:
    // 操作符重载
    T& operator[](size_t i){
        return m_arr[i];
    }
private:
    T m_arr[10];
};

template <class D> class Sum{ 
public:
    Sum(Array<D>& s):m_s(s){}
    D add(){     //求和
        D d = 0;
        for(int i = 0; i < 10; i++){
            d += m_s[i];
        }
        return d;
    }
private:
    Array<D> m_s; // 模板型成员变量
};

int main(void){
    Array<int> a;
    for (int i = 0; i < 10; i++){
        a[i] = i + 1;
    }
    // Sum 类模板实例化的类 Sum<int> 来实例化一个对象s,并用a来赋初始值
    Sum<int> s(a);
    cout << s.add()  << endl; //55
    return 0;
}

5.2 模板型成员函数

类模板的成员函数模板
在这里插入图片描述

  • foo是一个函数模板,又是一个成员函数, 可以直接叫 成员&函数模板
  • 说白了就是类模板里嵌套一个成员函数模板。那么用的时候就需要先实例化类,再实例化函数
#include <iostream>
using namespace std;
template <class T>
class A{
public:
    // 声明定义写一起
    template<class D> void foo(){       //成员函数模板
        cout << "A<T>::foo()" << endl;
    }
    // 声明定义分开写
    template<class X> void foo2();
};

// 既然是模板嵌套,那就有两个帽子。还要有作用域限定
template<class T>
template<class X> 
void A<T>::foo2(){
    cout << "A<T>::foo2()" << endl;
}

int main(void){
    // 1. 实例化类
    A<int> m;
    // 2. 实例化类
    m.foo<double>();    // A<T>::foo()

    m.foo2<double>();   // A<T>::foo2()

    return 0;
}

5.3 模板型成员类型

在这里插入图片描述

#include <iostream>
using namespace std;

// 类模板嵌套类模板
template <class X>class A{
public:
    template <class Y>class B{
    public:
        template <class Z>class C;
    };
};

// 类模板C在外部实现
template <class X>
template <class Y>
template <class Z> class A<X>::B<Y>::C{ // 翻过两次作用域
public:
    // 这里再来个成员函数模板, 用时需要实例化
    template<class T>void foo(){
        cout << "zhen e xin" << endl;
    }
};

int main(void){
    A<int>::B<int>::C<int> a;
    a.foo<int>();   // "zhen e xin"
    return 0;
}

5.4 模板型模板参数

在这里插入图片描述

  • <> 里的模板形参可以是 类型,数值,类模板
  • 上图中Arrary是一个类模板,它的模板参数是T
  • Sum也是一个类模板,它的模板参数是C,类型是一个类模板:template <class D> class ,缺省值是 类模板Arrary。这就是模板型模板参数
#include <iostream>
using namespace std;

template<class T>
class Array{
public:
    // 操作符重载
    T& operator[](size_t i){
        return m_arr[i];
    }
private:
    T m_arr[10];
};


template <class D, template<class M> class C> class Sum{ 
public:
    // Sum(Array<D>& s):m_s(s){}
    Sum(C<D>& s):m_s(s){}
    D add(){     //求和
        D d = 0;
        for(int i = 0; i < 10; i++){
            d += m_s[i];
        }
        return d;
    }
private:
    // Array<D> m_s;
    C<D> m_s; // 模板型成员变量
};

int main(void){
    Array<int> a;
    for (int i = 0; i < 10; i++){
        a[i] = i + 1;
    }
    // Sum 类模板实例化的类 Sum<int> 来实例化一个对象s,并用a来赋初始值
    Sum<int, Array> s(a);
    cout << s.add()  << endl; //55
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值