以文本方式查看主题 - 中文XML论坛 - 专业的XML技术讨论区 (http://bbs.xml.org.cn/index.asp) -- 『 C/C++编程思想 』 (http://bbs.xml.org.cn/list.asp?boardid=61) ---- C++/CLR泛型与C++模板的对比 (http://bbs.xml.org.cn/dispbbs.asp?boardid=61&rootid=&id=53028) |
-- 作者:卷积内核 -- 发布时间:9/24/2007 5:24:00 PM -- C++/CLR泛型与C++模板的对比 Visual Studio 2005把泛型编程的类型参数模型引入了微软.NET框架组件。C++/CLI支持两种类型参数机制--通用语言运行时(CLR)泛型和C++模板。本文将介绍两者之间的一些区别--特别是参数列表和类型约束模型之间的区别。 参数列表又回来了 参数在模板或泛型的定义中起占位符(placeholder)的作用。用户通过提供绑定到参数的实际值来建立对象实例。参数化类型的实例化并非简单的文本替代(宏扩展机制就是使用文本替代的)。相反地,它把实际的用户值绑定到定义中的相关的形式参数上。 在泛型中,每个参数都表现为Object类型或衍生自Object的类型。在本文后面你可以看到,这约束了你可能执行的操作类型或通过类型参数声明的对象。你可以通过提供更加明确的约束来调整这些约束关系。这些明确的约束引用那些衍生出实际类型参数的基类或接口集合。 模板除了支持类型参数之外,还支持表达式和模板参数。此外,模板还支持默认的参数值。这些都是按照位置而不是名称来分解的。在两种机制之下,类型参数都是与类或类型名称关键字一起引入的。 参数列表的额外的模板功能 非类型参数受常数表达式的约束。我们应该立即想到它是数值型或字符串常量。例如,如果选择提供固定大小的堆栈,你就可能同时指定一个非类型的大小参数和元素类型参数,这样就可以同时按照元素类别和大小来划分堆栈实例的类别。例如,你可以在代码片断1中看到带有非类型参数的固定大小的堆栈。 代码片断1:带有非类型固定大小的堆栈 template <class elemType, int size> public ref class tStack { array<elemType> ^m_stack; int top; public: tStack() : top( 0 ) { m_stack = gcnew array<elemType>( size ); } }; 此外,如果模板类设计者可以为每个参数指定默认值,使用起来就可能方便多了。例如,把缓冲区的默认大小设置为1KB就是很好的。在模板机制下,可以给参数提供默认值,如下所示: // 带有默认值的模板声明 template <class elemType, int size = 1024> public ref class FixedSizeStack {}; 用户可以通过提供明确的第二个值来重载默认大小值: // 最多128个字符串实例的堆栈 FixedSizeState<String^, 128> ^tbs = gcnew FixedSizeStack<String^, 128>; 否则,由于没有提供第二个参数,它使用了相关的默认值,如下所示: // 最多1024个字符串实例的堆栈 FixedSizeStack<String^> ^tbs = gcnew FixedSizeStack<String^>; 使用默认的参数值是标准模板库(STL)的一个基本的设计特征。例如,下面的声明就来自ISO-C++标准: // ISO-C++名字空间std中的默认类型参数值示例 { template <class T, class Container = deque<T> > class queue; template <class T, class Allocator = allocator<T> > class vector; // ... } 也可以提供默认的元素类型,如下所示: // 带有默认的元素类型的模板声明 template <class elemType=String^, int size=1024> public ref class tStack {}; 从设计的角度来说很难证明它的正确性,因为一般来说容器不会集中在在单个默认类型上。 指针也可以作为非类型参数,因为对象或函数的地址在编译时就已知了,因此是一个常量表达式。例如,你可能希望为堆栈类提供第三个参数,这个参数指明遇到特定条件的时候使用的回调处理程序。明智地使用typedef可以大幅度简化那些表面上看起来很复杂的声明,如下所示: typedef void (*handler)( ... array<Object^>^ ); template <class elemType, int size, handler cback > public ref class tStack {}; 当然,你可以为处理程序提供默认值--在这个例子中,是一个已有的方法的地址。例如,下面的缓冲区声明就提供了大小和处理程序: void defaultHandler( ... array<Object^>^ ){ ... } template < class elemType, int size = 1024, handler cback = &defaultHandler > public ref class tStack {}; 由于默认值的位置次序优先于命名次序,因此如果不提供明确的大小值(即使这个大小与默认值是重复的),也就无法提供重载处理程序。下面就是可能用到的修改堆栈的方法: void demonstration() { // 默认的大小和处理程序 tStack<String^> ^ts1 = nullptr; // 默认的处理程序 tStack<String^, 128> ^ts2 = gcnew tStack<String^, 128>; // 重载所有的三个参数 tStack<String^, 512, &yourHandler> ^ts3; } 模板支持的第二种额外的参数就是template模板参数--也就是这个模板参数本身表现为一个模板。例如: // template模板参数 template <template <class T> class arena, class arenaType> class Editor { arena<arenaType> m_arena; // ... }; Editor模板类列出了两个模板参数arena和arenaType。ArenaType是一个模板类型参数;你可以传递整型、字符串型、自定义类型等等。Arena是一个template模板参数。带有单个模板类型参数的任何模板类都可以绑定到arena。m_arena是一个绑定到arenaType模板类型参数的模板类实例。例如: |
-- 作者:卷积内核 -- 发布时间:9/24/2007 5:25:00 PM -- // 模板缓冲区类 template <class elemType> public ref class tBuffer {}; void f() { Editor<tBuffer,String^> ^textEditor; Editor<tBuffer,char> ^blitEditor; // ... } 类型参数约束 template <class T> ref class Demonstration { int method() { typename T::A *aObj; // ... } }; 这段代码成功地声明了aObj,它同时还约束了能够成功地绑定到你的类模板的类型参数。例如,如果你编写下面的代码,aObj的声明就是非法的(在这种特定的情况下),编译器会报错误信息: int demoMethod() { Demonstration<int> ^demi = gcnew Demonstration<int>( 1024 ); return dm->method(); } 当然,其特定的约束是,这个类型参数必须包含一个叫做A的类型的嵌套声明。如果它的名字叫做B、C或Z都没有关系。更普通的约束是类型参数必须表示一个类,否则就不允许使用T::范围操作符。我使用int类型参数同时违反了这两条约束。例如,Visual C++编译器会生成下面的错误信息: error C2825: ’T’: must be a class or namespace when followed by ’::’ C++模板机制受到的一条批评意见是:缺乏用于描述这种类型约束的形式语法(请注意,在参数化类型的原始设计图纸中,Bjarne Stroustrup论述了曾经考虑过提供显式约束语法,但是他对这种语法不太满意,并选择了在那个时候不提供这种机制)。也就是说,在一般情况下,用户在阅读源代码或相关的文档,或者编译自己的代码并阅读随后的编译器错误消息的时候,才能意识到模板有隐含约束。 如果你必须提供一个与模板不匹配的类型参数该怎么办呢?一方面,我们能做的事情很少。你编写的任何类都有一定的假设,这些假设表现为某些使用方面的约束。很难设计出适合每种情况的类;设计出适合每种情况和每种可能的类型参数的模板类更加困难。 另一方面,存在大量的模板特性为用户提供了"迂回"空间。例如,类模板成员函数不会绑定到类型参数,直到在代码中使用该函数为止(这个时候才绑定)。因此,如果你使用模板类的时候,没有使用那些使类型参数失效的方法,就不会遇到问题。 如果这样也不可行,那么还可以提供该方法的一个专门的版本,让它与你的类型参数关联。在这种情况下,你需要提供Demonstration<int>::方法的一个专用的实例,或者,更为普遍的情况是,在提供整数类型参数的时候,提供整个模板类的专门的实现方式。 一般来说,当你提到参数化类型可以支持多种类型的时候,你一般谈到的是参数化的被动使用--也就是说,主要是类型的存储和检索,而不是积极地操作处理它。 作为模板的设计人员,你必须知道自己的实现对类型参数的隐含约束条件,并且努力去确保这些条件不是多余的。例如,要求类型参数提供等于和小于操作是合理的;但是要求它支持小于或等于或XOR位运算符就不太合理了。你可以通过把这些操作分解到不同的接口中,或者要求额外的、表示函数、委托 |
W 3 C h i n a ( since 2003 ) 旗 下 站 点 苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》 |
48.828ms |