C++模板的特化与偏特化

https://www.cnblogs.com/yyehl/p/7253254.html

目录

一、函数模板

二、类模板

三、函数模板特例化

1、函数模板全特化

 2、函数模板重载(不存在偏特化)

四、类模板特例化

1、类模板全特化

2、类模板的偏特化

五、匹配顺序

六、类模板偏特化与STL


之前学习STL时接触过一段时间的模板,模板是C++泛型编程编程的基础
STL从头到尾都是模板泛型编程,我觉得用的最巧妙的就是在traits萃取技巧时用到的模板偏特化


  • 先简要回顾一下模板吧,模板主要分为函数模板与类模板

一、函数模板

template<class T>
T add(T a, T b) { return a + b;}

int main()
{
    int a = 1, b = 2;
    std::cout << add(a + b) << std::endl;
    return 0;
}

如上就是最简单的函数模板,当实例化函数模板时,编译器会自动进行实参类型推导
上面类型T就被自动推导为int类型

二、类模板

templete<class T>
class A
{
public:
    explicit A(T val) : t(val) { }
    T add(T x) { return t + y; }    
privete:
    T t;
};

int main()
{
    A<int> a(10);
    std::cout << a.add(5) << std::endl;
    return 0;
}

如上就是最简单的类模板,实例化类模板必须要指定类型,编译器无法为类模板自动推导类型

几个需要注意的点
1.类模板的和函数模板都必须定义在.h头文件中
2.模板的实例化类型确定是在编译期间
3.只是模板写好了,编译一般不会很多出错,出错一般会在实例化编译之后
4.模板实例化只会实例化用到的部分,没有用到的部分将不会被实例化


  • 我觉得模板的特例化是模板中比较精髓的东西
    有函数模板特例化,类模板特例化,其中又分为全特化与偏特化
    主要的用途都是对于特定的类型,指定特定的处理方式
    就相当于普通编程中if-else if - else这样的方式
    编译阶段确定如果是某个特化类型,就用特化的模板
    如果都不是,就用最一般的模板

三、函数模板特例化

函数模板只能全特化,不能偏特化,如果要偏特化的话只能重载

1、函数模板全特化

template< >                                  // 全特化 注意语法
double add(double a, double b)  { return a + b; }

int main()
{
    int x = 10, y = 20;
    double z = 1.1, w = 2.2;
    std::cout << add(x, y) << std::endl;   // 调用普通版本
    std::cout << add(z, w) << std::endl;   // 调用全特化版本
    return 0;
}

如果有与实参更加匹配的特例化版本,编译器将会选择特例化版本

 2、函数模板重载(不存在偏特化)

因为偏特化版本本质上仍然是模板,所以如果需要的话,可以重载一个函数模板

template<class T1>  // 重载版本,接收参数为指针
T1 add(T1* a, T1* b) { return *a + *b; }   
int main()
{
    int a = 10, b = 20;
    int *x = &a, *y = &b;
    add(a, b);    // 调用普通模板
    add(x, y);   // 调用重载的模板
    return 0;
}

如上,如果需要一个接收指针的偏特化版本,那么可以用重载模板实现(其实强行写偏特化版本,和模板函数重载版本一样)
函数模板不存在偏特化

四、类模板特例化

类模板既有全特化,又有偏特化
这里重新写一个更一般的模板类来说明类模板的特例化

1、类模板全特化

类模板全特化比较好理解,跟函数模板一样,全特化是一个实例,当编译器匹配时会优先匹配参数一致的实例

template< >        // 注意语法
class A<char*>       // 一个全特化的模板类A
{                    // 当用char*类型来实例化类模板A时,将会优先调用这个全特化实例
public:
    explicit A(char* val) : t(val) { }
    char* add(char* a, char* b) { return strcat(a, b); }
private:
    char* t;
};

2、类模板的偏特化

类模板的偏特化会稍微复杂一点点,它有多种形式
类模板偏特化本质上都是指定部分类型,让偏特化版本称为普通版本的子集,若实例化时参数类型为指定的类型,则优先调用特例化版本
第一种形式

template<class T1, class T2>      // 普通版本,有两个模板参数
class B { ..... };

template<class T2>            // 偏特化版本,指定其中一个参数,即指定了部分类型
class B<int , T2> { ..... };  // 当实例化时的第一个参数为int 则会优先调用这个版本

第二种形式,也是最重要的版本

template<class T>     // 普通版本
class B { ..... };

template<class T>   //这个偏特化版本只接收指针类型的模板实参 
class B<T*> { ..... }; 

template<class T>
class B<T&> { ..... };     // 这个偏特化版本只接受引用类型的模板实参

第三种形式

template<class T>    //普通版本
class B { ..... };

template<class T>   // 这种只接受用T实例化的vector的模板实参.也是一种偏特化
class B<vector<T>> { ......  };  

几个值得注意的地方
1.特例化本质上是我们顶替了编译器的工作,我们帮编译器做了类型推导
2.全特化本质上是一个
实例,而偏特化本质上还是一个模板,只是原来模板的一个子集
3.所以全特化的函数模板,本质上是实例,从而不会与函数模板产生二义性
4.若想让用户能使用特例化版本,特例化版本必须与模板定义在同一个.h头文件中

五、匹配顺序

类模板:全特化--》偏特化---》模板

函数模板:参数为int*举例,普通函数(完全匹配)---》全特化--》函数重载(T*--》模板



  • STL中的迭代器实现与高效实现与模板偏特化息息相关.

六、类模板偏特化与STL

偏特化在STL中最重要的两个应用

1.应用在迭代器设计中,为了使迭代器既可以萃取出值类型,又可以包容原生指针
如果要通过一个迭代器就能知道它的值类型,那么一般会使用iterator_traits
迭代器萃取技术的两个核心是:
1)在每个迭代器类中定义value_type值类型的类型成员,这样直接通过迭代器的value_type类型成员就可以知道值类型
2)问题就在于,迭代器必须兼容原生指针,而原生指针很难被重新定义,即要在原生指针的类中添加value_type的值类型的类型成员.这时候,靠的就是类模板的偏特化了.新添加一层iterator_traits类,专门萃取迭代器的属性,然后再对iterator_traits类设计原生指针与原生引用的偏特化版本,就解决了这个棘手的问题

2.type_traits类型萃取,对待特殊类型,特殊处理,提高效率
对于没有构造函数,析构函数等的内置类型,如果与复杂类型一样,执行同样的操作,显然是效率不高的
先实现一个对所有类型都设置一个最保守值的type_traits模板类,然后再对每个内置类型设置偏特化版本,内置类型设置一个更为激进的值,表明可以采取更为高效的操作来提高效率
比如copy函数,如果传递的对象是一个复杂类型,那么可能只能采取最保守的处理方式,一个一个的构造;如果是内置类型,这样显然太低效,使用memcpy()可能会好一些

其实iterator_traits也不止是处理兼容原生指针的问题,它也可以提高效率.
迭代器分为很多种,有可以随机访问的(vector),有只能前后一个一个移动的(list),也有只能单向移动的(slist),所以一般把迭代器分为五种:
InputIterator       输入迭代器
OutputIterator        输出迭代器
ForwardIterator      单向迭代器
BidirectionIterator     双向迭代器
RandomAccessIterator   随机访问迭代器
比如一个advance(n)函数,对于单向迭代器只能一个一个移动过去,但是这种实现对于随机访问迭代器显然不是理想的处理方式
处理的方式就是先实现这五个类,用作标记用,在每个迭代器里面都定义迭代器类型的类型成员iterator_catagory,再对不同版本的迭代器实现不同的advance(n)处理方式

猜你喜欢

转载自blog.csdn.net/songchuwang1868/article/details/89487031