【IT168 技术文档】
托管模板
也许你对泛型的概念已很清楚了,它帮助你避免进入C++的模板梦魇,它是实现模板的非常好的方式,等等。好,假设这些全部正确,C++/CLI支持泛型就象任何其它CLI语言一样-但是它有而其它一些CLI语言还没有的是它还支持托管模板-也就是模板化的ref和value类。如果你以前从未使用过模板,你不能一下欣赏这么多优点,但是如果你有模板使用背景而且你已发现了泛型中存在的可能限制你编码的方式,托管模板将会大大减轻你的负担。你能联合使用泛型和模板- 事实上有可能用一个托管类型的模板参数来实例化一个泛型类型(尽管相反的情形是不可能的,因为运行时刻实例化由泛型所用)。STL.net (或STL/CLR)以后讨论,请很好地利用泛型和托管模板的混合编程吧。
泛型使用的子类型约束机制将防止你写出下面的代码:
generic<typename T> T Add(T t1, T t2)
{ return t1 + t2; }
编译错误:
error C2676: binary ’+’ : ’T’ does not define this operator or a conversion to a type acceptable to the predefined operator
现在请看相应的模板版本:
template<typename T> T Add(T t1, T t2)
{ return t1 + t2; }
那么就可以这样做:
int x1 = 10, x2 = 20;
int xsum = Add<int>(x1, x2);
还可以这样做:
ref class R
{
int x;
public:
R(int n):x(n){}
R^ operator+(R^ r)
{ return gcnew R(x + r->x); }
};
//...
R^ r1 = gcnew R(10);
R^ r2 = gcnew R(20);
R^ rsum = Add<R^>(r1, r2);
这在一个象int的本机类型以及一个ref类型(只要ref类型有一个+运算符)情况下都能工作良好。这个泛型缺点不是一个调试错误或缺陷-它是设计造成的。泛型的实例化是在运行时通过调用配件集实现的,因此编译器不能确知一特定操作能被施行于一个泛型参数,除非它匹配一个子类型约束,因此编译器在定义泛型时解决这个问题。当你使用泛型时的另外一个妨碍是,它不会允许你使用非类型参数。下列泛型类定义不会编译:
generic<typename T, int x> ref class G{};
编译错:
error C2978: syntax error : expected ’typename’ or ’class’; found type ’int’; non-type parameters are not supported in generics
与托管模板相比较:
template<typename T, int x = 0> ref class R{};
如果你开始感激C++向你提供了泛型和托管模板,那么请看下面这一个例子:
template<typename T> ref class R{
public:
void F()
{ Console::WriteLine("hey"); }
};
template<> ref class R<int>
{
public:
void F()
{ Console::WriteLine("int"); }
};
你不能用泛型这样编码;否则,将产生:
编译错:error C2979: explicit specializations are not supported in generics
但可以在继承链中混合使用模板和泛型:
generic<typename T> ref class Base
{
public:
void F1(T){}
};
template<typename T> ref class Derived : Base<T>
{
public:
void F2(T){}
};
//...
Derived<int> d;
d.F1(10);
d.F2(10);
最后,你不能从一个泛型参数类型派生一个泛型类。
下列代码不会成功编译:
generic<typename T> ref class R : T
{};
error C3234: a generic class may not derive from a generic type parameter
模板让你这样做(好像你还不知道这些):
ref class Base{
public:
void F(){}
};
generic<typename T> ref class R : T
{};
//...
R<Base> r1;
r1.F();
这样,当你下次遇到对泛型的贬谤时,你就知道该怎么做了。