Modern C++(二)

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












郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。