MCD里面给我们展现了许多高阶的C++ template技巧,大部分都是天才级别的人才能想出来的,与此同时,这些技巧也稍显前卫了,至少根据我对国内C++程序员的了解,能把STL玩的很好的已经少见了,并且里面的大部分技巧是在“挑战编译器”。
但是,我还是决定好好把这本书看完,一边看一边摘录书中以及loki库中的代码进行测试,权当开阔自己的视野。
一.compile assert编译器断言技巧
引用:
// 以下是书中的代码
template<bool> struct CompileTimeError;
template<> struct CompileTimeError<true>{};
#define STATIC_CHECK(expr) \
(CompileTimeError< (expr) != 0>() )
template<bool> struct CompileTimeChecker
{
CompileTimeChecker(...);
};
template<> struct CompileTimeChecker<false> {};
#define STATIC_CHECK_MSG(expr, msg) \
{\
class ERROR_##msg {};\
(void)sizeof(CompileTimeChecker<(expr)> (ERROR_##msg()));\
}
// 以下是loki中的代码
namespace Loki
{
template<int> struct CompileTimeError;
template<> struct CompileTimeError<true> {};
}
#define STATIC_CHECK_LOKI(expr, msg) \
{ Loki::CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg;}
int main(int argc, char *argv[])
{
// gcc不能编译,VC7编译通过
STATIC_CHECK(1);
// gcc,VC7都不能编译
//STATIC_CHECK_MSG(1, Error_Msg);
// gcc,VC7都能编译
STATIC_CHECK_LOKI(1, Error_Msg);
return 0;
}
这个东东的技巧在于:定义一个模版类,这个模版的模版参数是bool类型,对true的特化模版类进行了定义,而false的特化类没有定义,在使用的时候把需要断言的表达式作为模版参数来初始化这个模版类,如果为false,因为false的特化类没有定义,此时编译器会报错。
注意:这里的断言是在编译期进行的,与一般的运行时断言有区别。
mik 回复于:2007-02-04 15:55:46
呵,弱弱地问:MCD是什么?
converse 回复于:2007-02-04 15:58:05
引用:原帖由 mik 于 2007-2-4 15:55 发表
呵,弱弱地问:MCD是什么?
弱弱的回答:您仔细看贴了么?
mik 回复于:2007-02-04 15:59:09
引用:原帖由 converse 于 2007-2-4 15:58 发表
弱弱的回答:您仔细看贴了么?
哈哈,确实没仔细看,明白了:oops:
sl_stanley 回复于:2007-02-04 19:31:52
当年霸占了同学一本,一直没看..........
converse 回复于:2007-02-04 23:13:43
二.检查是否可以转换以及是否存在继承关系的类
引用:
/********************************************************************
created: 2007/02/04
filename: CanConverse.cpp
author: Lichuang
purpose:
*********************************************************************/
#include <iostream>
using namespace std;
template <class T, class U>
class Conversion
{
private:
// sizeof(char) == 1,这是标准规定的
typedef char Small;
// sizeof(Big) == 2 * sizeof(char) > sizeof(char)
class Big { char dummy[2]; };
// Test型别是U,返回值是Small
static Small Test(U);
// Test型别是任意型别,返回值是Big
static Big Test(...);
// 返回一个T型别的值
static T MakeT();
public:
// exists的值反映了T是否可以转换为U
// MakeT首先返回一个T型别的值, 传入Test()中
// 如果编译器确定T可以转换为U,那么调用返回Small的Test函数
// 否则调用返回Big的Test函数,则sizeof(Big) != sizeof(Small)
// 对于一般的型别如int, char,double,float而言只是进行隐性的转换
// 但是对于传入类指针的情况而言,那么派生类的指针可以转换为基类的指针
// 也正是基于这一点可以作为检查两个类是否有继承关系的技巧
enum
{
exists = sizeof(Test(MakeT())) == sizeof(Small)
};
// 型别T,U是否可以双向转换
enum
{
exists2Way = exists && Conversion<U, T>::exists
};
// 是否是同一个型别
enum
{
sameType = false
};
};
// 针对同一个型别的模版偏特化类
template <class T>
class Conversion<T, T>
{
public:
enum
{
exists = 1,
exists2Way = 1,
sameType = 1
};
};
// 检查U是否是T的派生类的宏
// 第一条语句检查是否存在U*向T*的转换,如果存在上述关系那么派生类指针U*可以转换为基类指针T*
// 第二条检测派生类指针是不是和void*是一个型别的
#define SUPERSUBCLASS(T, U) \
(Conversion<const U*, const T*>::exists && \
!Conversion<const T*, const void*>::sameType)
class Base
{
};
class Derive
: public Base
{
};
class Test
{
};
int main()
{
cout << Conversion<double, int>::exists << ' ' << endl;
if (SUPERSUBCLASS(Base, Derive))
{
cout << "can derive\n";
}
if (!SUPERSUBCLASS(Base, Test))
{
cout << "Can not derive\n";
}
return 0;
}
说明的注释都在sample代码里面了,我想无需做太多的解释,直接看代码就好了。
converse 回复于:2007-02-08 00:25:41
三.typelists--递归思想的美妙体现
typelists是一个模板类,里面用模板存放了类型的信息,你可以通过这个类获得有关类型的信息,可以添加,删除,替换其中的类型,但是,这是一个没有数据成员的类,所有的操作都是在编译器进行的。
typelist的风格很像lisp,是递归思想的美妙体现。
类定义:
引用:
// 表示类型链表中的最后一位,相当于字符串中最后的'\0'
class NullType {};
// 头类型和尾类型都是模板
template <class T, class U>
struct Typelist
{
typedef T Head;
typedef U Tail;
};
链表中保存的类型都是模板,因此也可以把Typelist自己作为模板参数,也正是这个特性使得所有它的操作中递归思想体现的淋漓尽致。
NullType 是一个空类型,用于表示链表的末尾,有点类似于字符串中的'\0'。
以下是两个为了实现typelists方便而出现的宏和类:
引用:
// 仅保存一个类型的typelist
#define TYPELIST_1(T1) Typelist<T1, NullType>
// 根据传入的模板参数生成一个类型链表,注意默认类型是NullType
template<typename T1 = NullType, typename T2 = NullType, typename T3 = NullType>
struct MakeTypelist
{
private:
typedef typename MakeTypelist<T2, T3>::Result TailResult;
public:
typedef Typelist<T1, TailResult> Result;
};
// 对没有模板参数的偏特化类定义
template<>
struct MakeTypelist<>
{
typedef NullType Result;
};
[ 本帖最后由 converse 于 2007-2-8 00:29 编辑 ]
converse 回复于:2007-02-08 00:40:24
续上....
好了,现在开始查看与typelist的相关操作了.
前面说了,typelist是递归思想的美妙体现,因此在下面的相关操作中,都有一般情况,终止情况还有递推的情况,一般情况是针对最泛化的模板类实现,终止情况是针对nulltype(还记得我们前面说过nulltype表示链表的结束么)的实现,而递推情况是针对一般情况的递推。
1)返回typelist的长度的模板类
引用:
/********************************************************************
返回typelist长度的模板类
*********************************************************************/
template <class TList>
struct Length;
// 一般情况
template <class T, class U>
struct Length< Typelist<T, U> >
{
static const int value = 1 + Length<U>::value;
};
// 对于NullType返回0,这是终止情况
template <>
struct Length<NullType>
{
static const int value = 0;
};
Length的技巧在于:一个typelist的长度 = 1 + 除去头节点之后剩下的链表长度。回忆前面提到的typelist中的两个类型一个是head一个是tail,那么除去头节点之后剩下的链表就是tail了。
如果你看懂了上面的实现,也许已经开始嗅到一些递归的味道了吧~~
converse 回复于:2007-02-08 00:45:58
续上...
2)根据index返回在typelist中该index所在位置的类型的模板类
引用:
********************************************************************
根据index返回在一个typelist中位于这个index的类型,index从0开始
*********************************************************************/
template <class TList, int index>
struct TypeAt
{
typedef typename TypeAt<typename TList::Tail, index - 1>::Result Result;
};
template <class Head, class Tail>
struct TypeAt<Typelist<Head, Tail>, 0>
{
typedef Head Result;
};
这个模板类的思想与前面的length十分类似,比如:
一个typelist<int, typelist<char, double> >要求返回index=2的类型,
第一次调用为typelist<char, double>,index - 1 =1,
第二次调用为typelist<double,nulltype>, index - 1 = 0,此时head = double返回之~~
converse 回复于:2007-02-08 00:48:33
续上...
3)根据typelist中的类型返回index的模板类
引用:
/********************************************************************
根据typelist中的类型返回index,index从0开始
*********************************************************************/
template<class TList, class T>
struct IndexOf
{
private:
static const int temp = IndexOf<typename TList::Tail, T>::value;
public:
static const int value = (temp == -1 ? -1 : 1 + temp);
};
// 对于Typelist中的Tail返回0
template <class T, class Tail>
struct IndexOf<Typelist<T, Tail>, T>
{
static const int value = 0;
};
// 对nulltype返回-1
template <class T>
struct IndexOf<NullType, T>
{
static const int value = -1;
};
与typeat算法类似,不多阐述了。注意index是从0开始的。
converse 回复于:2007-02-08 00:54:08
困的不行,先把测试代码放上来,有兴趣的自己可以编译运行看看,gcc,vc7均编译通过了
引用:
/********************************************************************
created: 2007-2-7
filename: typelists.cpp
author: Lichuang
purpose:
*********************************************************************/
#include <iostream>
// 表示类型链表中的最后一位,相当于字符串中最后的'\0'
class NullType {};
// 头类型和尾类型都是模板
template <class T, class U>
struct Typelist
{
typedef T Head;
typedef U Tail;
};
#define TYPELIST_1(T1) Typelist<T1, NullType>
// 根据传入的模板参数生成一个类型链表,注意默认类型是NullType
template<typename T1 = NullType, typename T2 = NullType, typename T3 = NullType>
struct MakeTypelist
{
private:
typedef typename MakeTypelist<T2, T3>::Result TailResult;
public:
typedef Typelist<T1, TailResult> Result;
};
// 对没有模板参数的偏特化类定义
template<>
struct MakeTypelist<>
{
typedef NullType Result;
};
/********************************************************************
返回typelist长度的模板类,递归思想的美妙体现
*********************************************************************/
template <class TList>
struct Length;
//
template <class T, class U>
struct Length< Typelist<T, U> >
{
static const int value = 1 + Length<U>::value;
};
// 对于NullType返回0,这是起始情况
template <>
struct Length<NullType>
{
static const int value = 0;
};
/********************************************************************
根据index返回在一个typelist中位于这个index的类型,index从0开始
*********************************************************************/
template <class TList, int index>
struct TypeAt
{
typedef typename TypeAt<typename TList::Tail, index - 1>::Result Result;
};
template <class Head, class Tail>
struct TypeAt<Typelist<Head, Tail>, 0>
{
typedef Head Result;
};
/********************************************************************
根据typelist中的类型返回index,index从0开始
*********************************************************************/
template<class TList, class T>
struct IndexOf
{
private:
static const int temp = IndexOf<typename TList::Tail, T>::value;
public:
static const int value = (temp == -1 ? -1 : 1 + temp);
};
// 对于Typelist中的Tail返回0
template <class T, class Tail>
struct IndexOf<Typelist<T, Tail>, T>
{
static const int value = 0;
};
// 对nulltype返回-1
template <class T>
struct IndexOf<NullType, T>
{
static const int value = -1;
};
/********************************************************************
添加一个类型到typelist的后面
*********************************************************************/
template <class TList, class T>
struct Append;
// 在typelist中一直查找nulltype下去,找到的时候就把类型添加进去返回
template <class Head, class Tail, class T>
struct Append<Typelist<Head, Tail>, T>
{
typedef Typelist<Head, typename Append<Tail, T>::Result> Result;
};
template <class Head, class Tail>
struct Append<NullType, Typelist<Head, Tail> >
{
typedef Typelist<Head, Tail> Result;
};
template <class T>
struct Append<NullType, T>
{
typedef TYPELIST_1(T) Result;
};
template <>
struct Append<NullType, NullType>
{
typedef NullType Result;
};
/********************************************************************
移除typelist中某个类型的第一次出现
*********************************************************************/
// 当head == T的时候,这个查找过程结束,返回Tail
// 否则将一直查找下去
template<class TList, class T>
struct Erase
{
typedef Typelist<typename TList::Head, typename Erase<typename TList::Tail, T>::Result> Result;
};
template <class T, class Tail>
struct Erase<Typelist<T, Tail>, T>
{
typedef Tail Result;
};
template <class T>
struct Erase<NullType, T>
{
typedef NullType Result;
};
template<class TList, class T>
struct EraseAll
{
typedef Typelist<typename TList::Head,
typename EraseAll<typename TList::Tail, T>::Result> Result;
};
/********************************************************************
移除typelist中某个类型的所有出现
*********************************************************************/
template <class T, class Tail>
struct EraseAll<Typelist<T, Tail>, T>
{
typedef typename EraseAll<Tail, T>::Result Result;
};
template <class T>
struct EraseAll<NullType, T>
{
typedef NullType Result;
};
/********************************************************************
移除typelist中某个类型的重复出现,只留下最开始的一个
*********************************************************************/
template <class TList>
struct NoDuplicates;
template <class Head, class Tail>
struct NoDuplicates< Typelist<Head, Tail> >
{
private:
typedef typename NoDuplicates<Tail>::Result L1;
typedef typename Erase<L1, Head>::Result L2;
public:
typedef Typelist<Head, L2> Result;
};
template <>
struct NoDuplicates<NullType>
{
typedef NullType Result;
};
/********************************************************************
把typelist中某个类型替换为另一个类型
*********************************************************************/
template<class TList, class T, class U>
struct Replace
{
typedef Typelist<typename TList::Head, typename Replace<typename TList::Tail, T, U>::Result> Result;
};
template <class T, class Tail, class U>
struct Replace<Typelist<T, Tail>, T, U>
{
typedef Typelist<U, Tail> Result;
};
template <class T, class U>
struct Replace<NullType, T, U>
{
typedef NullType Result;
};
using namespace std;
int main()
{
typedef MakeTypelist<int, char, double>::Result typelist;
int i = Length<typelist>::value;
cout << "length of typelists is " << i << endl;
TypeAt<typelist, 2>::Result d = 0.123;
cout << "num = " << d << endl;
i = IndexOf<typelist, int>::value;
cout << "index of int in typelist is " << i << endl;
typedef Append<typelist, float>::Result typelist2;
i = IndexOf<typelist2, float>::value;
cout << "index of float in typelist2 is " << i << endl;
typedef Erase<typelist2, float>::Result typelist3;
i = IndexOf<typelist3, float>::value;
cout << "index of float in typelist3 is " << i << endl;
typedef MakeTypelist<int, char, char>::Result typelist4;
typedef EraseAll<typelist4, char>::Result typelist5;
i = IndexOf<typelist5, char>::value;
cout << "index of char in typelist5 is " << i << endl;
typedef NoDuplicates<typelist4>::Result typelist6;
i = IndexOf<typelist6, char>::value;
cout << "index of char in typelist6 is " << i << endl;
typedef Replace<typelist, double, unsigned char>::Result typelist7;
i = IndexOf<typelist7, unsigned char>::value;
cout << "index of unsigned char in typelist7 is " << i << endl;
return 0;
}
converse 回复于:2007-02-08 21:38:20
续上...
4)添加一个新类型到typelist中
引用:
/********************************************************************
添加一个类型到typelist的后面
*********************************************************************/
template <class TList, class T>
struct Append;
// 在typelist中一直查找nulltype下去,找到的时候就把类型添加进去返回
template <class Head, class Tail, class T>
struct Append<Typelist<Head, Tail>, T>
{
typedef Typelist<Head, typename Append<Tail, T>::Result> Result;
};
template <class Head, class Tail>
struct Append<NullType, Typelist<Head, Tail> >
{
typedef Typelist<Head, Tail> Result;
};
// 针对nulltype的偏特化,此时已经查找到链表尾,生成一个只含有一个类型的typelist返回
template <class T>
struct Append<NullType, T>
{
typedef TYPELIST_1(T) Result;
};
// 针对空链表的偏特化
template <>
struct Append<NullType, NullType>
{
typedef NullType Result;
};
查找typelist一直到查找到nulltype也就是链表结束的地方,生成一个只含有新增加的类型的typelist返回。
converse 回复于:2007-02-08 21:41:43
续上...
5)移除某个类型的第一次出现
引用:
/********************************************************************
移除typelist中某个类型的第一次出现
*********************************************************************/
// 当head == T的时候,这个查找过程结束,返回Tail
// 否则将一直查找下去
template<class TList, class T>
struct Erase
{
typedef Typelist<typename TList::Head, typename Erase<typename TList::Tail, T>::Result> Result;
};
// 当head == T时把tail返回
template <class T, class Tail>
struct Erase<Typelist<T, Tail>, T>
{
typedef Tail Result;
};
template <class T>
struct Erase<NullType, T>
{
typedef NullType Result;
};
当需要移除的类型为head时,把tail返回达到移除的效果。
converse 回复于:2007-02-08 21:46:31
续上....
6)移除某个类型的所有出现
引用:
/********************************************************************
移除typelist中某个类型的所有出现
*********************************************************************/
template<class TList, class T>
struct EraseAll
{
typedef Typelist<typename TList::Head, typename EraseAll<typename TList::Tail, T>::Result> Result;
};
// 继续在tail中删除类型T
template <class T, class Tail>
struct EraseAll<Typelist<T, Tail>, T>
{
typedef typename EraseAll<Tail, T>::Result Result;
};
template <class T>
struct EraseAll<NullType, T>
{
typedef NullType Result;
};
基本思想和erase类似,不同的是删除了需要删除的类型时不是直接返回而是继续查找下去
okmmno1 回复于:2007-02-09 08:31:12
难度太高,应者寥寥阿,楼主继续。
GoneWindy 回复于:2007-03-02 10:56:17
很有难度!
看的头疼!
lz加油
飞灰橙 回复于:2007-03-02 13:10:28
请教楼主,如何写一个通用的模板, 判断一个类是否是POD?
因为我想对一个搜索算法特化, 对于指向POD类型的random iterator,
可以有更快的搜索方法.
想了好久,无果。
我看过boost里的is_pod, 其实是几乎不能工作的.
|