19.5 explicit_cast(2)
explicit_cast的第一个局部特化版本是explicit_cast<T&>,它的构造函数和隐式转换操作符被声明为私有的,这有效地阻止了explicit_cast被用在任何引用上面。这挺好,但功能太强了,因为我们还想让它能够对基本类型的const引用也能够工作。这正是完全特化闪亮登场的时机。上面的代码中展示的是针对char的特化,另外还有针对其他所有基本类型的特化没有在代码中写出来。 最后,我们还需要对指针类型提供一个局部特化,因为如果没有这个局部特化的话,对于指针类型,编译器就必须在主模板以及针对引用的特化版本之间作出抉择,但有些编译器在这里会报告二义性错误。 于是,借助于这些特化版本,我们就可以分毫不差地得到我们想要的行为。
(注意:如果你想要禁止返回任何指向non-const实例的指针并想为此提供额外的警示,你可以像对引用所做的那样,把针对指针的特化版中的隐式转换操作符也改成私有的,并且进一步为const指针提供特化,也就是说,让explicit_cast<T const*>具有公有的构造函数和转换操作符。一句话,具体由你来定。)
遗憾的是,并非所有常用的编译器都支持局部特化,所以为那些不支持局部特化的编译器提供一个受限版本的explicit_cast是明智之举。你可以通过约束来达到目的。我们采用的方法是在析构函数里面放置constraint_must_be_pod()约束(见1.2.4小节),意思是该模板不能被用在非POD类型上。
程序清单19.7
- template <typename T>
- class explicit_cast
- {
- . . .
- #ifndef ACMELIB_TEMPLATE_PARTIAL_SPECIALIZATION_SUPPORT
- // 对于那些不支持局部特化的编译器,我们施加constraint_must_be_pod()约束
- ~explicit_cast()
- {
- constraint_must_be_pod(T);
- }
- #endif /* ! ACMELIB_TEMPLATE_PARTIAL_SPECIALIZATION_SUPPORT */
- . . .
- };
就这么多!它并不完美,因为它仍然允许该模板被用在非平凡的POD类型(例如大型结构)上。然而,由于我们已经决定,如果用户"按值"来参数化该模板,那么可能发生的拷贝行为所导致的开销就不是我们的责任了。如果在某些罕见的情况下,编译器不能将结构的中间拷贝优化掉,选择按值传递就是你自己的责任了。约束(记住,这是在编译期)的作用在于:拒绝任何可能具有高昂代价的拷贝操作的非平凡用户自定义类型。
explicit_cast的一个极好的特性是它不但可以被运用到那些提供了explicit_cast操作符的类型上,还能够被运用到那些提供了隐式转换操作符的类型上。这就意味着,不管某个类型是被小心谨慎地编写的还是粗枝大叶地编写的,你都可以编写出针对它们的同样有效的泛型代码,因而explicit_cast就可以成为泛型转换机制的基础,而对于是否转换、转换至什么类型这两个问题,选择权在程序员的手中,而不是编译器。事情本该如此,不是吗?