2.1 编译期Assertions
表达式在编译期评估所得结果是一个定值(常数),这意味着你可以利用编译器(而非代码)来做检查。这个想法是传给编译器一个语言构造,如果是非零表达式便合法。于是当你传入一个表达式而其值为0时,编译器会发出一个编译期错误的信息。
最简单的方式称为compile-time assertions。他依赖于一个事实:大小为0的array是非法的。
#define STATIC_CHECK {char unnamed[(expr)?1:0];} template <class To, class From> To safe_reinterpret_cast(From from) { STATIC_CHECK(sizeof(from)<=sizeof(To)); return reinterpret_cast<To>(from); } ... void somePointer=...; char c=safe_reinterpret_cast<char>(somePointer);
此时,如果在你的系统中,指针大小大于字符,编译器会抱怨你正试着产生一个长度为0 的array。
问题是,你收到的错误消息无法正确表达信息。较好的解法是以来一个名称带有意义的template。
template<bool> struct ComplieTimeError; template<> struct ComplieTimeError<true> {}; #define STATIC_CHECK(expr) (ComplieTimeError<expr>!=0>())
ComplieTimeError需要一个非型别参数,而且它只针对true有所定义。如果你试着具现化ComplieTimeError<false>,编译器会发出“Undefined specialization ComplieTimeError<false>”消息,这个消息比错误消息好,因为它是我们故意制造的,不是编译器或程序的bug。 为了更好的定制错误消息,可以传入一个额外引数给STATIC_CHECK,并让它在错误消息中出现,唯一分缺点是这个定制消息必须是合法的C++标示符,这个想法引出了一个改良版的CompileTimeChecker
template<bool> struct ComplieTimeChecker { ComplieTimeChecker(...); }; template<> struct ComplieTimeChecker<false> {}; #define STATIC_CHECK(expr,msg) { class ERROR_##msg{}; (void)sizeof(ComplieTimeChecker<(expr)>(ERROR_##msg())); }
假设sizeof(char)<sizeof(void*),
template<class To, class From> To safe_reinterpret_cast(From from) { STATIC_CHECK(sizeof(From)<=sizeof(To)), Destination_Type_Too_Narrow); return reinterpret_cast<To>(from); } ... void* somePointer=...; char c=safe_reinterpret_cast<char>(somePointer);
处理完毕以后,上述的safe_reinterpret_cast会被展开成下列样子:
template<class To, class From> To safe_reinterpret_cast(From from) { { class ERROR_Destination_Type_Too_Narrow{}; (void)sizeof(ComplieTimeChecker<(sizeof(From)<=sizeof(To))> (ERROR_Destination_Type_Too_Narrow())); } return reinterpret_cast<To>(from); }
这段程序定义了一个名为ERROR_Destination_Type_Too_Narrow的local class(空类),然后生成 一个性别为
ComplieTimeChecker<(sizeof(From)<=sizeof(To))>的暂时对象,并以一个型别为ERROR_Destination_Type_Too_Narrow的暂时对象加以初始化。最终sizeof会测量出这个对象的大小。 2.2 Patial Tempalte Specialization
template<class Window, class Controller> class Widget { ...generic implemention... };
你可以向下面这样加以特化
template<> class Widget<ModalDialog,MyController> { ...specialized implemention... };
其中ModalDialog和MyController是你另外定义的classes。
有了这个widget特化定义之后,如果你定义widget<ModalDialog,MyController>对象,编译器就是用上述定义,如果你定义其他泛型对象,编译器就会使用原本的泛型定义。
如果你想要针对任意window并搭配一个特定的mycontroller来特化widget。这是就需要partial template specialization机制:
template<class Window> class Widget<Window,MyController> { ...partially specialized implemention... };
注:1、虽然你可以全特化class template中的成员函数,当你不能偏特化它们。
2、你不能偏特化namespace-level函数,最接近namespace-level template function偏特化机制的是函数重载,就实际运用而言那就意味着你对函数参数有很精致的特化能力。
2.3 Local classes
void Fun() { class Local { ...member variables... ...member function definitions... }; ...code using Local... }
不过还是有一些限制,local calsses不能定义static成员变量,也不能访问non-static局部变量,它可以在template函数中被使用,定义于template韩函数内的local classes可以运用函数的template参数。
class Interface { public: virtual void Fun()=0; ... }; template<class T,class P> Interface* MakeAdapter(const T& obj,const P& arg) { class Local:public Interface { public: Local(const T& obj,const P& arg) :obj_(obj),arg_(arg){} virtual void Fun() { obj_.Call(arg_); } private: T obj_; P arg_; }; return new Local(obj,arg); }
事实证明,任何也能用local classes的手法,都可以改用函数外的template classes来完成。换言之,并非一定得local classes不可,不过local classes 可以简化实作并提高符号的地域性。