首页 > 代码库 > 泛型编程深入探索之一,模版高级特性小结

泛型编程深入探索之一,模版高级特性小结

 

一些基本的模版特性:

非类参数模版

模版所声明的参数可以不是类参数,可以声明的非类参数包括整数(double,float不可以),enum,对象引用或指针。

通过模版嵌套实现类或非类参数载类方法参数上的重载(调用时实现,不在定义时实现)

 

友元函数模版:

直接举例:

template <typename T> 

friend ostream& operator<< <T>(ostream& os, const T& t){};

 

内联模版函数:

模版定义必须在头文件中,所以所定义的函数并不直接是内联的,需要确定声明该函数是内联的。

inline void func(){};    //在模版类定义中

inline void A<T>:: func{};  //在模版类定义外

 

高级模版特性:

模版嵌套

 

当在某个模版类的参数调用时,无论是类参数还是非类参数,需要另一个模版参数,使用模版嵌套。

值得注意的时,模版嵌套不能handle当参数模版的参数和原模版的参数为同一个类的情况,因此,必须要保留无模版嵌套的原始情况完成模版方法函数的重载。

 

另外,模版嵌套的优先级是:

对于未使用参数模版,即参数全部使用原模版参数的方法,单独定义,单独调用。

一旦参数中使用了嵌套模版,则一定触发某个嵌套模版方法,触发的顺序是,

触发仅改变所有模版化参数的方法重载,如果没有,则逐级触发包括了更多未改变参数的嵌套模版。

 

测试代码如下:

template <typename T,int SCORE>class vec{    T a;    int sc;public:    T getA()const {return a;}    int getScore() const {return sc;}    vec(T t):a(t),sc(SCORE){};    //参数中未使用模版嵌套的拷贝构造,必须有    vec(const vec<T,SCORE>& vv):a(vv.a),sc(vv.sc){cout<<"copyT is called"<<endl;};    //参数score模版化的拷贝构造    template <int OTHER>    vec(const vec<T,OTHER>& vv):a(vv.getA()),sc(vv.getScore()){cout<<"copyOther is called"<<endl;};    //参数t和score均发生模版化的拷贝构造    template <typename E,int OTHER>    vec(const vec<E,OTHER>& vv):a(vv.getA()),sc(vv.getScore()){cout<<"copy is called"<<endl;};    virtual ~vec();};

 

 

模版偏特化与函数模版重载偏特化

 

模版通过重写一个具体类实现模版的偏特化(即某个具体类不按照模版实现),函数没有偏特化的功能,因此,通过重载实现。

优先级均为:

确定类,确定指针/引用/对象的模版类,任意模版类的顺序调用。

 

特殊的,模版全特化

所有模版参数都被特化,此时,函数可以特化,且特化类的函数重写不需要加上模版声明template<>

 

偏特化测试代码

#include <iostream>template <typename T>class pointertemp{public:    T name;    pointertemp():name(T()){std::cout<<"original ctr callled"<<std::endl;};};template <typename T>class pointertemp<T*>{public:    T* name;    pointertemp():name(nullptr){std::cout<<"T* ctr callled"<<std::endl;};};template <>class pointertemp<char*>{public:    char* name;    pointertemp():name(nullptr){std::cout<<"char* ctr callled"<<std::endl;};};template <typename K>void showK(K name){    std::cout<<"K func called";}template <typename K>void showK(K* name){    std::cout<<"K* func called";}void showK(char* name){    std::cout<<"char* func called";}

值得注意的是,当T为指针或者引用时,在找不到匹配的具体类的特化实现时,编译器会取找有没有指针特化或者引用特化,这些都优先于未特化模版。

 

且在指针特化和引用特化时,用<A*>实例化,但模版参数其实是A,也就是说<A*>实例化实际上是将A赋予T,然后使用指针特化模版。

 

模版继承:

模版类可以实现继承和多态,一般的,子类继承父类的同参数实例类,如果要继承任意类参数的实例类,只需要在模版中多声明一个父类类参数即可。

 

模版参数模版

模版嵌套用来模版化类模版的方法参数,特化用来重载模版的某一个特例类型的实现或者重载一个类集合;

 

常用于声明一个模版,他的参数是一个模版类实例

基本语法如下,当使用了模版参数模版后,模版的模版参数在模版的定义内实例化,不需要外部实现。

用来实例化模版参数的类可以是任意确定类或者模版声明的其他类参数。

 

模版参数模版的意义在于,在模版定义时即确定了其参数模版的参数,不需要在调用时由被调用者确定,应区别下面三种模版的声明:

//通过模版参数模版,使得模版的参数模版可以使用任意类,但该类在原模版的定义中实例化确定。template <typename T, template<typename E> class container>class temptemp{    //模版参数在这里实例化};//调用: temptemp<int ,vector>//模版参数模版使用确定类,该类需要用户自己声明,但只能声明确定类,一旦声明其他类,编译器会报错template <typename T, typename container<T>>class A;//调用: temptemp<int ,vector<int>>//调用: temptemp<int ,vector<double>>是编译错误的template <typename T, typename container<E>>class A;//调用: temptemp<int ,vector<int>>是编译错误的//调用: temptemp<int ,vector<double>>//上述两个可实现模版重载

 

基本用法如下:

template <typename T, template<typename E, typename allocator=allocator<E>> class container>class temptemp{public:    container<T> vt;    container<int> vint;    void show(){        vt.resize(10);        vint.resize(10);        vt.at(0)=3;        vint.at(0)=12;        std::cout<<vt[0]<<"\t"<<vint[0]<<endl;    }};

 

类型推导模版:

 

区别auto和decltype

 

二者均返回一个类型名,其返回值用法与int或者自定义的A没有区别。

 

二者的区别在于:

decltype(expr)在运行这句时动态决定一个类型,内部要一个确定的表达式(不能含有模版)。

auto 是占位符,表示这里所需要确定的类型在之后变量初始化或函数声明后确定。

变量直接通过初始化确定,函数返回值通过trailing确定,即在函数声明后加上-> decltype(expr)(此时,可以由模版对象,因为模版编译到此刻时已经确定类型了)或确定的变量与表达式。

 

auto参不允许出现在函数原型中!

 

考察这样的一个类型推导型的模版:

template <typename T,typename E>auto func (const T& a,const E& b) ->decltype(a*b){    auto s=decltype(a*b)();    return s;}

 

#未完待续

 

泛型编程深入探索之一,模版高级特性小结