托管C++中的范型和模板的区别
今天看到一段奇特的托管C++代码 (下面的的代码是随手写的类似代码)
void TestFunc(T param)
{
//....
}
熟悉C#的人一看就了解,这不就是C#里面的范型吗?(习惯于C++的我很拗口地说)
没错就是范型,不过这是托管C++的版本。之前我一篇托管C++随笔里面有提到托管C++里面其实支持template的。
这两个东东功能上是一样的,可以让函数,类支持各种各样的参数。但是这两者之间又有什么区别呢?
一头雾水,那就google吧(顺便抱怨一下,google从中国大陆撤出以后,经常访问出错…………不知道是不是只有我这里这样)
结果中文网站中基本没看到有提到这两者区别的,只好找E文的了。google.com上面就发现了不少的文章/讨论,世界大了,什么人都有哈
这里有一篇不错的文章:How do C# generics compare to C++ templates?
MSDN上也有一篇很详细的文章:An Introduction to C# Generics,这个可以选中文语言
两者的本质区别在于:template是编译时处理的,generic则是运行时。
- template在代码编译的过程中会检查具体化的模板参数类型,如果这个类型之前没有使用过,编译器就会使用新类型作为参数根据模板代码生成一套新的代码。所以如果使用同一个模板类的类型越多,生成的代码体积就越大。而且template类通常是要求放在头文件里面的,这样编译器才会知道在使用新类型具体化这一模板类的时候怎么生成代码。当然如果只是在当前cpp文件中使用的话,也可以在cpp中写。另外要注意的一个问题,template的代码只有当你用到它了,编译器才会用具体类型来检查你的模板代码是否正确。如果完全没有用到,或者是用的类型正好符合一些要求,模板代码中的就不会被查出
template <class T>
class Test
{
public:
int TestFunc(T a)
{
return a.Func();
}
}这个模板类如果完全没有用到,或者你是用一个声明了int Func()函数的类作模板参数,编译器并不会报错,如果你用其他类型来使用这个模板,编译器就会立即报错。有点类似宏
-
generic则是在编译阶段作一些基本检查,并不会根据不同的参数类型生成不同的代码,而是把范型代码编译成IL,类型参数会使用占位符表示。当代码运行时需要使用范型代码时,.NET会根据需要的参数类型以及之前编译好的IL代码生成实际的运行代码。而.NET生成实际的运行代码的过程包含两种情况:
-
如果需求的类型是值类型,JIT编译器会检查这一类型是否已经生成过运行代码了,如果是则直接返回之前的引用,否则就生成新的。
-
如果需要的类型是引用类型,JIT编译器会使用Object来生成运行代码,并且保存,之后所有引用类型都会使用这一份代码。
-
-
generic不能使用基本数据类型作为约束!这个让我很无语,不知道为什么……也就是下面的代码是不能用的
generic <typename T> where T:System::Int32
T TestFunc(T a, T b)
{
return a + b;
}ref class Test
{
public:
void Func()
{
Console.WriteLine("aa");
}
};
ref class Derived
{
public:
generic <typename T> where T:Test
void TestFunc(T a)
{
a->Func();
}
};
Derived^ p = gcnew Derived();
p->TestFunc<Test^>((Test^)p);注意上面调用代码的写法以及TestFunc的写法,虽然你知道传给TestFunc的参数应该是引用,但是TestFunc不能用T^作为参数进行声明,否则编译器会报错,而且a->Func()也不能写成a.Func(),否则也会编译出错。调用函数的时候则需要用Test^作为参数调用,不能用Test(我尝试过没成功…………)。