![]() |
|
||||||||||||||
| | 网站首页 | 数据库教程 | web编程 | 服务器 | 程序设计 | | ||
|
||
|
|||||||
| Pure C++:泛型编程:模板特殊化 | |||||||
作者:佚名 文章来源:不详 点击数: 更新时间:2007-8-1 ![]() |
|||||||
|
在上一期专栏中我已经谈到过,执行的操作不仅包括简单存储和检索操作的参数化类型仅限于可安全绑定到它的可接受类型 [请参阅 Pure C++: CLR Generics Versus C++ Templates(英文)]。
图 2 显示了 min 的一种可能的实现方法。我将定义一个局部变量 min_val 来存放最小元素,并将它初始化为容器的第一个元素。然后定义两个迭代程序,将每个元素与 min_val 进行比较,如果其值比 min_val 小则为 min_val 重新赋值。现在,您能看出隐含的限制吗?如果能,则您会得到: if ( *it < min_val ) 通常,对于 min 函数,只有能够使用内置小于 (<) 运算符的类型或本身具有 operator<() 实例的类型才能绑定到 elemType 的类型。如果某个类型没有定义 operator<(),并尝试对此类型的项的 tStack 调用 min,则在 min 中使用无效的比较运算符时将出现编译时错误。例如,System::String 类没有小于 (<) 运算符(它使用 IComparable 的 CompareTo 方法)。因此,如果我尝试对使用 String 实例化的 tStack 调用 min,则它在编译时就会出错,因为该比较操作失败了。 有一种解决方案我不会使用:定义全局运算符 operator<(),该运算符使用 CompareTo 来比较两个 String 类型的值。然后,tStack<String^>::min() 会自动调用这些全局运算符: bool operator<( String^ s1, String^ s2 ) {return ( s1->CompareTo( s2 ) < 0 ) ? true :false;} 请记住,目标是防止当用户指定的类型参数为 String 时实例化 tStack::min 成员函数定义,而希望使用 CompareTo 方法来定义自己的 tStack<String^>::min 实例。您可以使用显式模板特殊化定义为类模板实例化的成员提供特殊化的定义,来实现此目的。此定义指明了模板名称、指定模板的参数、函数参数列表和函数主体。关键字模板的后面是小于 (<) 和大于 (>) 标记,然后是类成员特殊化的定义(请参阅图 3)。 即使类的类型 tStack<String^> 是从常规类模板定义(即由编译器内部生成的专用于 String 的实例,其中每个 elemType 占位符都被替换为 String 类型)实例化的,类型 tStack<String^> 的每个对象都会调用特殊化的成员函数 min。tStack::min 成员函数定义既不会被扩展,也不会在 tStack<String^> 中使用。 在有些情况下,可能整个类模板定义都不适合某种类型。在这种情况下,程序员可以提供一种定义来特殊化整个类模板。程序员可以提供 tStack<String^> 的定义: template <class elemType>ref class tStack;// 类模板特殊化template<> ref class tStack<String^> {public:tStack();String^ pop();void push( Stack^ et ); // ...}; 只有在声明了常规类模板后,才能定义显式类模板特殊化。如果您提供完整的类模板特殊化,则必须定义与此特殊化关联的每个成员函数或静态数据成员。类模板的常规成员定义决不能用于创建显式特殊化的成员定义,也不会被交叉检查。这是因为类模板特殊化的类成员集可能与常规模板的类成员集完全不同。 定义完全特殊化的类模板(如 tStack<String^>)的成员时,请勿在其定义前添加特殊的 template<> 标记,而应该通过显式列出实际的类型来指明特殊化定义,如下所示: // 定义类模板特殊化的// 成员函数 min()String^ tStack<String^>::min() { ... }
如果类模板有多个模板参数,您可以针对一个或一组特定的参数化值或类型来特殊化类模板。也就是说,您可能希望提供一个模板,使其除了某些模板参数已被实际类型或实际值替换以外,其他均与常规模板匹配。使用局部模板特殊化就可以实现此目的。例如,假设存在下面的 Buffer 类模板: template <class elemType, int size>ref class Buffer { ... }; 下面说明如何对 Buffer 使用局部特殊化,使其能够很好地处理大小为 1KB 的缓冲区: // 类模板 Buffer 的局部特殊化template <class elemType>ref class Buffer<elemType,1024> {// 对 1KB 大小使用特殊算法...}; Buffer 的局部特殊化只有一个类型参数 elemType,因为大小的值固定为 1024。局部模板特殊化的参数列表只列出了模板参数仍然未知的参数。但是,当您定义该模板的实例时,必须同时指定这两个参数(这与对一个参数使用默认值的情形不同)。在下面的示例中,局部类模板特殊化是用 elemType 为 String 的类型参数实例化的: Buffer<String^,1024> mumble; 但是,如果您改为下面的代码行,则编译器会生成错误,并将声明标记为缺少第二个参数: Buffer<String^> mumble; // 错误 为什么会这样呢?如果开发人员以后引入一组特殊化的 Buffer(如下所示),会出现什么情况? template <class elemType>ref class Buffer<elemType,4096> {};template <class elemType> ref class Buffer<elemType,512> {}; 如果前面示例的声明中不要求使用第二个参数,编译器就无法区分这几种特殊化! 局部特殊化与其对应的完整常规模板同名,在本例中为 Buffer。这就带来了一个有趣的问题。请注意,Buffer<String^,1024> 的实例化既可以通过类模板定义进行,也可以通过局部特殊化进行。那么,为什么会选择局部特殊化来实例化该模板呢?一般的规则是:如果声明了局部类模板特殊化,编译器就会选择最特殊化的模板定义进行实例化。只有在无法使用局部特殊化时,才会使用常规模板定义。 例如,当必须实例化 Buffer<String^,2048> 时,由于此实例化与任何一个局部模板特殊化都不匹配,因此会选择常规模板定义。 局部特殊化的定义完全不同于常规模板的定义。局部特殊化可以拥有一组与常规类模板完全不同的成员。局部类模板特殊化的成员函数、静态数据成员和嵌套类型必须有自己的定义,这与类模板特殊化相同。类模板成员的常规定义决不能用于实例化局部类模板特殊化的成员。 类模板的局部模板特殊化构成了现代 C++ 用法中一些非常复杂的设计惯用语的基础。如果您对此感兴趣,可以阅读 Andrei Alexandrescu 撰写的《Modern C++ Design: Generic Programming and Design Patterns Applied》(Addison-Wesley,2001 年版),了解此用法的详细信息。
|
|||||||
| 文章录入:admin 责任编辑:admin | |||||||
| 【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口】 | |||||||
| 网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!) |
| | 设为首页 | 加入收藏 | 联系站长 | 友情链接 | 版权申明 | 网站公告 | 网站地图 | 管理登录 | | |||
|