C# 泛型与 C++ 模板的比较

20

5
真巧,这正是我今天博客的主题。 http://blogs.msdn.com/ericlippert/archive/2009/07/30/generics-are-not-templates.aspx - Eric Lippert
1
这必须是完全重复的副本。 @Eric Lippert:哦,有趣,我会读一下。 - jalf
5个回答

33
你可以将C++模板视为一个伪装成泛型系统的解释性函数式编程语言。如果这不让你感到害怕,那么它应该会让你感到害怕 :)
C#泛型非常受限制;你只能在类上对类型进行参数化,并在方法中使用这些类型。所以,以MSDN的例子来说,你可以这样做:
public class Stack<T>
{
   T[] m_Items; 
   public void Push(T item)
   {...}
   public T Pop()
   {...}
}

现在您可以声明一个Stack<int>Stack<SomeObject>,它将安全地存储该类型的对象(即,不用担心错误地放入SomeOtherObject)。
在内部,.NET运行时将其专门化为基本类型(如int)和对象类型的变体。例如,这使得Stack<byte>的表示比Stack<SomeObject>小得多。
C++模板允许类似的用法:
template<typename T>
class Stack
{
    T *m_Items;
    public void Push(const T &item)
    {...}
    public T Pop()
    {...}
};

这一看起来与之前类似,但有几个重要的区别。首先,与每种基本类型和所有对象类型都有一个变体不同,它针对实例化的每种类型都有一个变体。这可能是很多类型!
下一个主要区别是(在大多数C++编译器上),它将在每个使用它的翻译单位中编译。这可能会严重降低编译速度。
C++模板的另一个有趣属性是它们可以应用于除类以外的其他事物 - 当它们被应用时,它们的参数可以自动检测。例如:
template<typename T>
T min(const T &a, const T &b) {
  return a > b ? b : a;
}

类型T将自动由函数使用的上下文确定。

这些属性可以用于好的目的,但会牺牲您的理智。因为C++模板每使用一种类型就重新编译一次,并且模板的实现始终可供编译器使用,所以C++可以对模板进行非常激进的内联处理。再加上在函数中自动检测模板值的功能,您可以使用boost::lambda在C++中创建匿名伪函数。因此,像这样的表达式:

_1 + _2 + _3

生成一个非常可怕的类型对象,该对象具有一个operator(),它将其参数相加。

C++模板系统还有许多其他黑暗角落-它是一种非常强大的工具,但思考起来可能很痛苦,有时很难使用-特别是当它给您提供了长达二十页的错误消息。 C#系统要简单得多-功能较弱,但更易于理解和难以滥用。


2
C++的模板另一个有趣的属性就是它们可以应用于除类以外的其他事物。这与C#允许我们在函数中定义泛型(例如:T SomeFunc<T>(T input){...})是相同的吗? - dotNET
C++模板会更快执行,因为它们被编译成C++的机器码(可能是因为它们是编译器宏,而不是过度通用)。但是C#对象不必重新编译可以放置在dll中。如果您正在开发允许动态代码执行的程序,则可以在运行时使用插入的C#泛型对象或函数,而无需重新编译。我认为这个技巧在C++中实现起来更难,因为它的模板化函数或事物必须编译才能得到不同的实例。 - John Foll

5

2
实际上,尽管泛型可以在运行时通过反射(就像其他任何东西一样)实例化,但很多内容都是在编译时确定的。例如,JIT会生成特定的List<int>实现,而不是使用等效于List<object>的通用类型擦除版本(这是它与Java不同的一种方式)。 - Daniel Earwicker
@Earwicker:跟我说说吧。这就是为什么你不能进行通用推广(即将List<int>连接到List<object>)。该功能在CLI中存在,但他们选择不以C#的方式实现它(在我看来是个错误)。 - Paul Sonier

2

看起来这个问题是重复的。谢谢指点。 - Bjarke Freund-Hansen

1

这篇Eric Gunnerson的博客文章很好地涵盖了这个主题。

最大的直接区别在于模板是编译时特性,而泛型是运行时特性。


-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接