近日,在学习的过程中第一次接触到了Typelist的相关内容,比如Loki库有一本Modern C++ design的一本书,大概JD搜了一波没有译本,英文版600多R,瞬间从价值上看到了这本书的价值!!这是题外话。这本书十分经典。其内容对于一个C++新手来说需要时间来理解吸收。在这里记录一下自己的理解。日后发现错误会给予更正。如有有人碰巧看到了。欢迎指正。

参考了http://blog.csdn.net/gatieme/article/details/50953564

整篇内容分了三个部分:1.特化  2.Typelist 3.应用的情形

1.在说明Typelist相关内容之前,要先了解一下什么叫模板特化与模板偏特化。

  1.1 模板与特化:

    模板分为函数模板与类模板。函数模板是一种抽象函数的定义,它代表了具有相同结构的一类函数。类模板类似于Stack等封装区分数据类型,是一种更高级的抽象封装

    所谓特化就是讲泛型的东西更加的具体化,比如在某些泛型参数中进行限定,使得不受任何约定的模板参数受到了约束(比如常见的这个大写T),下面的例子中会更具象化的说明个人的一些理解。

    特化的分类:分为函数模板特化和类模板特化,全特化与偏特化

    ①:函数模板特化

      当函数模板需要对某些类型进行限定的时候称之为函数模板特化

    ②:类模板特化

      与上述类似,只是是使用于类

    ③:全特化

      将模板中的参数全部指定为确定的类型,其标志就是应用于完全确定的内容。而不是在编译时去确定具体的应用实例。标志:template<>然后是和末班类型没有关系的类实现或者函数定义。

    ④:偏特化

      模板中的参数没有被全部指定。需要编译器在编译时进行确定。

  1.2 函数模板特化:

    如下代码:

template <class T>
int compare(const T left,const T right) {
std::cout << "test template func" << endl;
return (left - right);
}

这个函数能够满足一些基本类型的比较需求(int,float,....巴拉巴啦),但是对于字符串的比价这个函数是不能支持的。

因此我们可以对其进行特化处理。

template < >
int compare<const char *>(const char * left,const char * right) {
std::cout << "function tempate special" << std::endl;
return strcmp(left,right);
} //另一种特化方式是如下
template < >
int compare(const char * left,const char * right) {
  std::cout << " in special template <> .." << std::endl;
  return strcmp(left,right);
}

测试代码:

#include<bits/stdc++.h>

template <class T>
int compare(const T left,const T right) {
std::cout << "test template func" << std::endl;
return (left - right);
} template <>
int compare(const char* left, const char* right){
std::cout <<"in special template..." <<std::endl;
return strcmp(left, right);
} int main() {
std::cout << compare(, ) << std::endl;
const char *left = "abcd";
const char *right = "accd";
std::cout << compare(left, right) << std::endl;
return ;
}

输出内容

test template func
-
in special template...
-

函数的特化,当函数调用发现有特化后的匹配函数的时候,会优先调用特化的函数。而不是通过函数模板进行实例化。

  1.2类模板特化

    与函数模板特化类似,当模板内需要对某些类型进行特别处理时,需要这种处理。这里归纳了一个模板参数的类模板特化的几种类型。

    1.绝对类型

    2.引用,指针类型

    3. 特化为另一个类模板(这个厉害了,我猜的)

  1.2.1 特化为绝对类型 : 直接为某个特定类型做特化,这是一种常见的方式。

    

#include <iostream>
#include <cstring>
#include <cmath> template <class T>
class Compare {
public :
static bool IsEqual(const T & lh,const T & rh) {
std::cout << "uniusall " << std::endl;
return lh == rh;
}
}; template<>
class Compare<float> {
public :
static bool IsEqual(const float & lh,const float & rh) {
std::cout << "float special class " << std::endl;
return abs(lh - rh) < 1e-;
}
}; int main() {
Compare<int> comp1;
std::cout << comp1.IsEqual(,) << std::endl;
Compare<float> comp2;
std::cout << comp2.IsEqual(,) << std::endl;
}

另外我特意要说明的是,如果没有第一段template<class T>的模板声明,直接template<> Class Compare<float>是否可以?

这个是不可以的,编译报错内容是 'Compare' is not a class template,这个在后边有关typelist内容中也会提出(ps 主要是由在阅读Typelist中的一行代码导致我特意测试了一下这种情况)。

偏特化:

template <class T1,class T2>
class A {}; template <class T1>
class A<T1,int> {};

下面的代码框内容是在另一个博客中提到的另外2中类型,目前还没有使用过。作为记录放在这里

template <class _Iterator>
struct iterator_traits {
typedef typename _Iterator::iterator_category iterator_category;
typedef typename _Iterator::value_type value_type;
typedef typename _Iterator::difference_type difference_type;
typedef typename _Iterator::pointer pointer;
typedef typename _Iterator::reference reference;
}; // specialize for _Tp*
template <class _Tp>
struct iterator_traits<_Tp*> {
typedef random_access_iterator_tag iterator_category;
typedef _Tp value_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef _Tp& reference;
}; // specialize for const _Tp*
template <class _Tp>
struct iterator_traits<const _Tp*> {
typedef random_access_iterator_tag iterator_category;
typedef _Tp value_type;
typedef ptrdiff_t difference_type;
typedef const _Tp* pointer;
typedef const _Tp& reference;
};
// specialize for T*
template<class T>
class Compare<T*>
{
public:
static bool IsEqual(const T* lh, const T* rh)
{
return Compare<T>::IsEqual(*lh, *rh);
}
};

这种特化其实是不一种绝对的特化,他只是对类型做了某些限定,但仍然保留了莫版型,给我们提供了极大地方便。

在这里,我么不就不需要对int*.float *,double *等等类型分别做特定的特化。这其实是第二种方式的扩展,其实夜视对类型做了某种限定。而不是绝对化为某个具体类型。

如下一段代码

template <class T>
class Compare< vector<T> > {
public :
static bool IsEqual(const vector<T> &lh,const vector<T> & rh) {
if (lh.size() != rh.size()) return false;
else {
for (int i = ; i < lh.size() ; i++)
if (lh[i] != rh[i]) return false;
}
}
};

上述的代码比较好理解。就省略了

以下是第三种特化为另一个类模板

template <class T1>
struct SpecializedType {
T1 x1;
T1 x2;
} template <class T>
class Compare< SpeciallizedType<T> > {
public :
static bool IsEqual(const Specialized<T> & lh,const Specialized<T>&rh) {
return Compare<T>::IsEqual(lh.x1 + lh.x2,rh.x1 + rh.x2);
}
}; SpecializedType<float> a = {10.0f,10.1f};
SpecializedType<float> b = {10.3f,10.4f};
bool flag = Compare<SpecializedType<float> >::IsEqual(a,b);

2.关于TypeList,这个是来自于Loki库中的一部分。

基于个人的理解。我分开一段一个一个函数的记录一下。

首先是一些最基本的定义和宏

class NullType {};
template <class T,class U>
struct Typelist {
typedef H Head;
typedef U Tail;
}
//通过定义一些宏使得typelist线性化
#define TYPELIST_0() NullType
#define TYPELIST_1(T1) Typelist<T1,TYPELIST_0()>
#define TYPELIST_2(T1,T2) Typelist<T1,TYPELIST_1(T2)>
#define TYPELIST_3(T1,T2,T3) Typelist<T1,TYPELIST_2(T2,T3)>
#define TYPELIST_4(T1,T2,T3,T4) Typelist<T1,TYPELIST_3(T2,T3,T4)>
#define TYPELIST_5(T1,T2,T3,T4,T5) Typelist<T1,TYPELIST_4(T2,T3,T4,T5)>

Typelist结构里面是2个typedef,看见其内部没有任何数值,他们的实体是空的,不含有任何状态,也未定义任何函数。执行期间Typelists也不带任何数值,他们的存在只是为了携带类别信息,Typelist并未打算被具体化。 另外规定,typelist必须以NullType作为结尾。其可以被视为一个结束符号。具体宏的作用结合下面的例子来说明。

//如何声明使用
typedef TYPELIST_0() TL0;
typedef TYPELIST_3(char,int,double) TL3;
将上面的宏产开后
是如下的形式 typedef NullType TL0;
typedef Typelist<char,Typelist<int,Typelist<double,NullType> > >TL3;

上面这种方法利用了特化中的特化为另一个模板的方法。

针对于上面的展开,可以看下获取长度length的代码

//为了方便解释,我把他们分为3部分.
//第一部分 只有一行
template<class TList>struct Length;
//第二部分
template<>struct Length<NullType> {
enum { value = ; }
};
//第三部分
template<class T,class U>
struct Length<Typelist<T,U> > {
enum { value = + Length<U>::value} ;
};

分开来解释一个我个人的理解:

第一部分:template<class TList>struct Length;

这句话实际上是最困扰我的一句话,首先这句话一定要有。否则的代价是编译不过。

关于这句话的作用个人的理解第一:基于编译是否通过,如果使用全特化,必然要有“前置的模板声明”,否则会报错

                                        第二:首先说明这个获取长度的方法该如何调用。基于前面讲的TL3

std::cout<<Length<TL0>::value<<std::endl;

借助这个调用来解释我个人的理解,在Length中,只有NullType和Typelist<T,U>可以进行匹配,当我们尝试传递Length<int>的时候自然是无法找到匹配, 原因是Length进行特化的时候只能匹配到NullType和Typelist,

由此,这句话既是声明又是一种限定,他告诉编译器什么形式的具象化可以匹配模板。

第二个部分:

  全特化,只有NullType可以匹配,递归调用的终点。很好理解

第三个部分:

  偏特化,结合前面的宏展开,可以看出其递归调用的方式。其使用Typelist来进行特化,需要2个参数。

剩下的代码大体只是逻辑区别:不再赘述,完整代码:(来自http://blog.csdn.net/zhuyingqingfen/article/details/43938713)

#ifndef TYPE_LISTS_H_
#define TYPE_LISTS_H_ #include <iostream>
#include <string>
#include "typetraits.h" /*
TypeLists 内部没有任何数值(value),他们的实体是空的,不含有任何状态,也未定义任何函数。
执行期间TypeLists也不带任何数值,他们存在的理由只是为了携带型别信息。TypeLists 并未打算被具
现化。因此,当我们说“a TypeListL”,实际指的是一个typelist型别,不是一个typelist 对象。
规定 typelist 必须以NullType(类)结尾,NullType可被视为一个结束符号,类似于c字符串的\0功能,
定义一个只有一个元素的typelist如下:
typedef Typelist<int,NullType> OneTypeOnly.
*/
template<class T,class U>
struct Typelist
{
typedef T Head;
typedef U Tail;
};
Class NullType{};
//通过定义宏 将typelist线性化
#define TYPELIST_0() NullType
#define TYPELIST_1(T1) Typelist<T1,TYPELIST_0()>
#define TYPELIST_2(T1,T2) Typelist<T1,TYPELIST_1(T2)>
#define TYPELIST_3(T1,T2,T3) Typelist<T1,TYPELIST_2(T2,T3)>
#define TYPELIST_4(T1,T2,T3,T4) Typelist<T1,TYPELIST_3(T2,T3,T4)>
#define TYPELIST_5(T1,T2,T3,T4,T5) Typelist<T1,TYPELIST_4(T2,T3,T4,T5)> //计算TypeList长度
//大多数Typelist的操作都是基于递归,递归终止条件通过模板特化实现。
template<class TList>struct Length;
template<>struct Length<NullType>//Length的全特化,即,只匹配NullType。
{
enum{value = };
};
template<class T,class U>
struct Length<Typelist<T,U> >//Length的扁特化,可匹配任何TypeList<T,U>类型,包括U同时也是Typelist的复合情况。
{
enum{value = +Length<U>::value};
};
//2 索引式访问
template <class TList,unsigned int index> struct TypeAt;
template<class Head,class Tail>
struct TypeAt<Typelist<Head,Tail>,>
{
typedef Head Result;
};
template<class Head,class Tail,unsigned int i>
struct TypeAt<Typelist<Head,Tail> ,i>
{
typedef typename TypeAt<Tail,i->::Result Result;
}; //类似TypeAt功能,不过TypeAtNonStrict对逾界访问更加宽容。
//比如TypeList的个数是3,那么你不能使用TypeAt<TL3,3>::Result,这样会编译错误。
//但是TypeAtNonStrict<TL3,3,NullType>::Result可以,如果不存在索引为3的type,那么结果是第三个引数即NullType
template <class TList, unsigned int i, typename DefType = NullType>
struct TypeAtNonStrict
{
typedef DefType Result;
};
template <class T, class U, typename DefType>
struct TypeAtNonStrict< Typelist<T, U>, , DefType >
{
typedef T Result;
};
template <class T, class U, unsigned int i, typename DefType>
struct TypeAtNonStrict< Typelist<T, U>, i, DefType >
{
typedef typename TypeAtNonStrict<U, i - , DefType>::Result Result;
}; //3 查找TypeList
template<class TList,class T> struct IndexOf;//声明
template<class T>
struct IndexOf<NullType,T>//如果TList为NullType,那么令value = -1;
{
enum{value = -};
};
template<class Tail,class T>
struct IndexOf<Typelist<T,Tail> ,T>//如果T是TList中的头端,那么令value= 0;
{
enum{value = };
};
template<class Head,class Tail,class T>//将IndexOf施于TList尾端和T,并将结果置于一个临时变量temp
struct IndexOf<Typelist<Head,Tail> ,T>//如果temp为-1,令value为-1,否则令value为1+temp
{
private:
enum{temp = IndexOf<Tail,T>::value};//temp要先于value声明定义。
public:
enum{value = temp == - ? - : temp + };
}; //4 附加元素到typelist
template <class Tlist,class T>struct Append;//声明
template<>struct Append<NullType,NullType>//如果TList是NULL而且T是NULL,那么令Result为NullType
{
typedef NullType Result;
};
template <class T> struct Append<NullType,T> //如果TList是NullType,且T是type(非typelist),
{ //那么Result将是"只含有唯一元素的T";
typedef TYPELIST_1(T) Result;
};
template <class Head,class Tail>
struct Append<NullType,Typelist<Head,Tail> >// 如果TList是NullType,且T是一个typelist,那么Result便是T本身
{
typedef Typelist<Head,Tail> Result;
};
template<class Head,class Tail,class T>//否则,如果Tlist是non-null,那么result将是个typelist,以TList::Head
struct Append<Typelist<Head,Tail>,T> //为起头端,并以T附加到TList::Tail的结果为其尾端。
{
typedef Typelist<Head,typename Append<Tail,T>::Result> Result;
}; //5 Reverse
template <class TList> struct Reverse;
template <>struct Reverse<NullType>
{
typedef NullType Result;
};
template <class Head, class Tail>
struct Reverse< Typelist<Head, Tail> >
{
typedef typename Append<
typename Reverse<Tail>::Result, Head>::Result Result;
}; #endif

调用测试代码:

void typelists_test()
{
typedef TYPELIST_0() TL0;
typedef TYPELIST_3(char,int,double) TL3;
typedef TYPELIST_3(char,int,double) TL3_1;
//Length
std::cout<<Length<TL0>::value<<std::endl;
std::cout<<Length<TL3>::value<<std::endl; //TypeAt
typedef TypeAt<TL3,>::Result Parm1;
typedef TypeAt<TL3,>::Result Parm2;
typedef TypeAt<TL3,>::Result Parm3; typedef TypeAtNonStrict<TL3,,EmptyType>::Result TEST_TYPE; std::cout<<"Parm1 Type:"<<typeid(Parm1).name() <<" sizeof : "<< sizeof(Parm1)<<std::endl;
std::cout<<"Parm2 Type:"<<typeid(Parm2).name() <<" sizeof : "<< sizeof(Parm2)<<std::endl;
std::cout<<"Parm3 Type:"<<typeid(Parm3).name() <<" sizeof : "<< sizeof(Parm3)<<std::endl;
std::cout<<"TEST_TYPE Type:"<<typeid(TEST_TYPE).name() <<" sizeof : "<< sizeof(TEST_TYPE)<<std::endl; //IndexOf
std::cout<<"char indexof TL3 :"<<IndexOf<TL3,char>::value<<std::endl;
std::cout<<"int indexof TL3 :"<<IndexOf<TL3,int>::value<<std::endl;
std::cout<<"float indexof TL3 :"<<IndexOf<TL3,float>::value<<std::endl; //Append
typedef Append<TL3,int> TL4;//TL4不是一个TypeList
typedef Append<TL3_1,TYPELIST_2(float,double)> TL5;
std::cout<<"TL4 Length :"<<Length<TL4::Result>::value<<std::endl;
std::cout<<"TL5 Length :"<<Length<TL5::Result>::value<<std::endl; //Reverse
std::cout<<"Reverse result:"<<typeid(Reverse<TL3>::Result).name()<<std::endl;
}

3.应用

  举例:大学绩点计算,众所周知的这是一个基于权重的计算方法,对于不同学分的学科权重不同。现在假设有4科目吧:信号,电磁场,高频,微波器件

那么可能的实现方式是这样的:(PS觉得这个例子不太恰当甚至十分不恰当,仅作为个人辅助理解。)

①:直接计算

//伪代码
if (当前的科目是信号) {
信号科目相关加权和分数处理...
}
if (当前的科目是电磁场) {
电磁场科目相关加权和分数处理...
}
//剩下的略

②:利用类和继承来实现。

  第一种的实现很直接却脱离了面向对象的设计,显得代码十分多,乱。

  第二种大体代码就像下面,在这里创建了对应的实例化对象进行处理。

#include<bits/stdc++.h>
using namespace std; class ScoreBase {
public :
ScoreBase() {}
virtual int calcuWeight(score * scores);
virtual ~ScoreBase() {}
} class ScoreSignal : public ScoreBase {
public :
ScoreSignal(){}
virtual int calcuWeight(score * scores);
} class ScoreElect : public ScoreBase {
public :
ScoreElect() {}
virtual int calcuWeight(score * scores);
} vector<ScoreBase *>dealscore;
dealscore.push_back(new ScoreSignal());
dealscore.push_back(new ScoreElect());
score * scores = get_scores();//获取到了分数
int totalWeight = 0;
for (int i = ; i < (int)dealscore.size() ; i++)
totalWeight += dealscore[i] -> calcuWeight(scores);

③:使用typelist完成这件事情

记得之前的展开宏么,这就可以使用到它了。为了方便,我们用结构体(纯粹是因为默认public,关于在C++中struct和class的区别,http://www.cnblogs.com/Commence/p/7481315.html)

第一步先通过宏定义出我们想要的东西

struct signalscore {
int static calcuWeight(score * scores);
} struct elecscore {
int static calcuWeight(score * scores);
} struct highfreqscore {
int static calcuWeight(score * scores);
} typedef Typelist<signalscore,Typelist<elecscore,Tyeplist<highfreqscore,NullType> > >calWeightList;

第二步:类似前面获取Length的方法,建立模板来处理它。

template<class TList>struct calWeight;
template<>struct calWeight<NullType> {
int static calcuWeight(scene * scenes) { return ;}
} template<class T,class U>
struct calWeight< Typelist<T,U> > {
int static calWeight(scene * scenes) {
return T::calcuWeight(scene * scenes) + calWeight<U>::calWeight(scene * scenes);
}
} std::cout << calWeight<calWeightList>::calWeight(scenes) << std::endl;

由于时间关系:上述第三种并没有完整的可以编译通过的代码。将在近期补充

C++ 模板特化以及Typelist的相关理解的更多相关文章

  1. C++(VS2015)模板显式特化之template语法深入理解

    首先说下遇到的情况: 这里在vc++6.0上建立了一个自定义模板类,再去覆盖这个类,分别使用部分覆盖,整体覆盖 但在vs2015上去整体覆盖类会报错. 错误如下: 错误原因:个人感觉是新版本的vs更接 ...

  2. 转:C++模板特化的概念

    http://blog.csdn.net/yesterday_record/article/details/7304025 很久没有看C++,在看STL源码剖析时,看到一个function templ ...

  3. C++ template —— 模板特化(五)

    本篇讲解模板特化-------------------------------------------------------------------------------------------- ...

  4. C++模板之隐式实例化、显示实例化、隐式调用、显示调用和模板特化详解

    模板的实例化指函数模板(类模板)生成模板函数(模板类)的过程.对于函数模板而言,模板实例化之后,会生成一个真正的函数.而类模板经过实例化之后,只是完成了类的定义,模板类的成员函数需要到调用时才会被初始 ...

  5. C++面向对象编程之成员模板、模板特化、偏特化和模板模板参数

    1.成员模板 理解起来就是类是模板类,他里面的成员函数又是个模板函数 上图例子:用派生类构造父类 2.模板特化 就是在类模板泛化后写特化的类,在template<>里不绑定类型,而在类后面 ...

  6. C++-函数模板特化如何避免重复定义

     我正在用一个基于模板的库源代码,该库包含一些针对特定类型的模板函数特化.类模板,函数模板和模板函数特化都在头文件中.我在我的.cpp文件中 #include 头文件并编译链接工程.但是为了在整个工程 ...

  7. C++ Primer 学习笔记_84_模板与泛型编程 --模板特化

    模板与泛型编程 --模板特化 引言: 我们并不总是能够写出对全部可能被实例化的类型都最合适的模板.某些情况下,通用模板定义对于某个类型可能是全然错误的,通用模板定义或许不能编译或者做错误的事情;另外一 ...

  8. C++ Primer 学习笔记_85_模板与泛型编程 --模板特化[续]

    模板与泛型编程 --模板特化[续] 三.特化成员而不特化类 除了特化整个模板之外,还能够仅仅特化push和pop成员.我们将特化push成员以复制字符数组,而且特化pop成员以释放该副本使用的内存: ...

  9. C++程序设计方法4:模板特化

    模板参数的具体化/特殊化 有时,有些类型不适用,则需要对模板进行特殊化处理,这称为“模板特化” 对函数模板,如果有多个模板参数,则特化时必须提供所有参数的特例类型,不能部分特化: 如: char *s ...

随机推荐

  1. django学习系列-01

    安装Django > pip install django==1.10.3(py2)或者>python3 -m pip install django==1.10.3(py3) 成功安装 D ...

  2. SQL 临时表或表变量替代游标(转)

    1.如果表没有自动增长的标识列(int) 使用临时表 SELECT IDENTITY(int) NewID ,.. INTO #tmp FROM YouTable 2.表有标识列 使用表变量 INSE ...

  3. [TJOI2008]彩灯 线性基

    题面 题面 题解 题意:给定n个01串,求互相异或能凑出多少不同的01串. 线性基的基础应用. 对于线性基中的01串,如果我们取其中一些凑成一个新的01串,有一个重要的性质:任意2个不同方案凑出的01 ...

  4. 【CF666E】Forensic Examination(后缀自动机,线段树合并)

    [CF666E]Forensic Examination(后缀自动机,线段树合并) 题面 洛谷 CF 翻译: 给定一个串\(S\)和若干个串\(T_i\) 每次询问\(S[pl..pr]\)在\(T_ ...

  5. 【BZOJ1391】Order(网络流,最小割)

    [BZOJ1391]Order(网络流,最小割) 题面 BZOJ权限题... 良心洛谷 题目描述 有N个工作,M种机器,每种机器你可以租或者买过来. 每个工作包括若干道工序,每道工序需要某种机器来完成 ...

  6. CF487E Tourists 【圆方树 + 树剖 + 堆】

    题目链接 CF487E 题解 圆方树 + 树剖 裸题 建好圆方树维护路径上最小值即可 方点的值为其儿子的最小值,这个用堆维护 为什么只维护儿子?因为这样修改点的时候就只需要修改其父亲的堆 这样充分利用 ...

  7. 【ZJOI2005】沼泽鳄鱼 题解报告

    题目描述 潘塔纳尔沼泽地号称世界上最大的一块湿地,它地位于巴西中部马托格罗索州的南部地区.每当雨季来临,这里碧波荡漾.生机盎然,引来不少游客. 为了让游玩更有情趣,人们在池塘的中央建设了几座石墩和石桥 ...

  8. 多线程中join方法的含义

    1.作用:调用这个方法的时候,主进程会在这里停住,等待该线程进行完毕再继续往下执行. 如:不使用join的情况: <?php class Join extends Thread { public ...

  9. linux中使用随机数

    (1)单纯使用rand重复调用n次,就会得到一个0-RAND_MAX之间的伪随机数,如果需要调整范围,可以得到随机数序列后再进行计算.(2)单纯使用rand来得到伪随机数序列有缺陷,每次执行程序得到的 ...

  10. HTML+css零碎小知识

    1.设置了float浮动的元素和绝对定位position:absolute的元素会脱离正常的文档流.但是设置absolute的元素不会占据空间,相当于隐形了.   2.相对定位position:rel ...