Bjarne:为什么不能为模板参数定义约束?
2008-02-23 05:24:02来源:互联网 阅读 ()
看看这个:
template<class Container> void draw_all(Container& c) { for_each(c.begin(),c.end(),mem_fun(&Shape::draw)); } |
假如出现类型错误,可能是发生在相当复杂的for_each()调用时。例如,假如容器的元素类型是int,我们将得到一个和for_each()相关的含义模糊的错误(因为不能够对对一个int值调用Shape::draw的方法)。
为了提前捕获这个错误,我这样写:
template<class Container> void draw_all(Container& c) { Shape* p = c.front(); // accept only containers of Shape*s for_each(c.begin(),c.end(),mem_fun(&Shape::draw)); } |
对于现在的大多数编译器,中间变量p的初始化将会触发一个易于了解的错误。这个窍门在很多语言中都是通用的,而且在任何的标准创建中都必须这样做。在成品的代码中,我也许能够这样写:
template<class Container> void draw_all(Container& c) { typedef typename Container::value_type T; Can_copy<T,Shape*>(); // accept containers of only Shape*s for_each(c.begin(),c.end(),mem_fun(&Shape::draw)); } |
这样就很清楚了,我在建立一个断言(assertion)。Can_copy模板能够这样定义:
template<class T1, class T2> struct Can_copy { static void constraints(T1 a, T2 b) { T2 c = a; b = a; } Can_copy() { void(*p)(T1,T2) = constraints; } }; |
Can_copy(在运行时)检查T1是否能够被赋值给T2。Can_copy<T,Shape*>检查T是否是Shape*类型,或是个指向由Shape类公共继承而来的类的对象的指针,或是被用户转换到Shape*类型的某个类型。注意这个定义被精简到了最小:
一行命名要检查的约束,和要检查的类型
一行列出指定的要检查的约束(constraints()函数)
一行提供触发检查的方法(通过构造函数)
注意这个定义有相当合理的性质:
您能够表达一个约束,而不用声明或复制变量,因此约束的编写者能够用不着去设想变量如何被初始化,对象是否能够被复制,被销毁,连同诸如此类的事情。(当然,约束要检查这些属性的情况时例外。)
使用现在的编译器,无需为约束产生代码
定义和使用约束,无需使用宏
当约束失败时,编译器会给出可接受的错误信息,包括“constraints”这个词(给用户一个线索),约束的名字,连同导致约束失败的周详错误(例如“无法用double*初始化Shape*”)。
那么,在C 语言中,有没有类似于Can_copy——或更好——的东西呢?在《C 语言的设计和演变》中,对于在C 中实现这种通用约束的困难进行了分析。从那以来,出现了很多方法,来让约束类变得更加容易编写,同时仍然能触发良好的错误信息。例如,我信任我在Can_copy中使用的函数指针的方式,他源自Alex Stepanov和Jeremy Siek。我并不认为Can_copy()已能够标准化了——他需要更多的使用。同样,在C 社区中,各种不同的约束方式被使用;到底是哪一种约束模板在广泛的使用中被证实是最有效的,还没有达成一致的意见。
但是,这种方式很普遍,比语言提供的专门用于约束检查的机制更加普遍。无论如何,当我们编写一个模板时,我们拥有了C 提供的最丰富的表达力量。看看这个:
template<class T, class B> struct Derived_from { static void constraints(T* p) { B* pb = p; } Derived_from() { void(*p)(T*) = constraints; } }; template<class T1, class T2> struct Can_copy { static void constraints(T1 a, T2 b) { T2 c = a; b = a; } Can_copy() { void(*p)(T1,T2) = constraints; } }; template<class T1, class T2 = T1> struct Can_compare { static void constraints(T1 a, T2 b) { a==b; a!=b; a<b; } Can_compare() { void(*p)(T1,T2) = constraints; } }; template<class T1, class T2, class T3 = T1> struct Can_multiply { static void constraints(T1 a, T2 b, T3 c) { c = a*b; } Can_multiply() { void(*p)(T1,T2,T3) = constraints; } }; struct B { }; struct D : B { }; struct DD : D { }; struct X { }; int main() { Derived_from<D,B>(); Derived_from<DD,B>(); Derived_from<X,B>(); Derived_from<int,B>(); Derived_from<X,int>(); Can_compare<int,float>(); Can_compare<X,B>(); Can_multiply<int,float>(); Can_multiply<int,float,double>(); Can_multiply<B,X>(); Can_copy<D*,B*>(); Can_copy<D,B*>(); Can_copy<int,B*>(); } // 典型的“元素必须继承自Mybase*”约束: template<class T> class Container : Derived_from<T,Mybase> { // ... }; |
事实上,Derived_from并不检查来源(derivation),而仅仅检查转换(conversion),但是这往往是个更好的约束。为约束想一个好名字是很难的。
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇: C 箴言:争取异常安全的代码
下一篇: C 箴言:理解inline化的介入和排除
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash