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 可以简化实作并提高符号的地域性。