嗯,总的来说,C++ 模板和 C# 泛型相似 - 与 Java 泛型相比,完全不同,但它们也有很大的不同。与 C# 一样,通过使用反射获得运行时支持,获取描述用于实例化泛型的类型的对象。 C++ 没有反射,它对类型所做的一切都是在编译时完成的。
C# 泛型和 C++ 模板之间的最大区别确实是 C# 泛型具有更好的类型检查功能。它们总是受到约束,因为它们不允许在定义泛型时未声明有效的操作。 C# 的首席设计师提出了隐含约束会增加复杂性的原因。我不太熟悉C#,所以我不能在这里多说。我将讨论 C++ 中的问题以及它们将如何改进,这样人们就不会认为 C++ 的东西都是错的。
在 C++ 中,模板不受约束。如果您执行操作,则在模板定义时暗示该操作将在实例化时成功。 C++ 编译器甚至不需要在语法上检查模板的有效性。如果它包含语法错误,则必须在实例化时诊断该错误。在此之前的任何诊断都是实施的纯粹好处。
这些隐含的约束在短期内对模板设计者来说很容易,因为他们不必关心在模板界面中说明有效的操作。他们将负担放在模板的用户身上——因此用户必须确保他满足所有这些要求。通常情况下,用户尝试看似有效的操作但失败了,编译器会向用户提供数百行关于某些无效语法或未找到名称的错误消息。因为编译器一开始不知道什么约束被违反了,它列出了错误位置周围的所有代码路径部分,甚至所有不重要的细节,用户将有爬过可怕的错误消息文本。
这是一个基本问题,可以通过在模板或泛型的接口处说明类型参数必须具有的属性来解决。据我所知,C# 可以约束参数以实现接口或继承基类。它在类型级别上解决了这个问题。
C++ 委员会早就看到有必要解决这些问题,很快(可能是明年),C++ 也将有办法声明这些明确的约束(看时间-下面的机器注释),如下例所示。
template<typename T> requires VariableType<T>
T f(T a, T b) {
return a + b;
}
编译器此时会发出错误信号,因为所写的表达式未被要求标记为有效。这首先帮助模板的设计者编写更多正确的代码,因为代码已经在某种程度上进行了类型检查(以及那里可能的情况)。程序员现在可以说明该要求:
template<typename T> requires VariableType<T> && HasPlus<T, T>
T f(T a, T b) {
return a + b;
}
现在,它将编译。编译器通过看到T 作为返回类型出现,自动暗示T 是可复制的,因为T 的使用出现在界面中,而不是在模板主体中。其他要求使用要求条款说明。现在,如果用户使用未定义op+ 的类型,他将收到相应的错误消息。
C++1x 将需求与类型分离。以上适用于原始类型以及类。从这个意义上说,它们更灵活,但也相当复杂。规定何时以及何时满足要求的规则很长......您可以使用新规则说以下内容:
template<typename T> requires MyCuteType<T>
void f(T t) { *t = 10; }
然后,使用int 致电f!只需为MyCuteType<int> 编写一个概念图,它就可以教编译器如何取消引用 int。它会在这样的循环中变得非常方便:
for_each(0, 100, doSomething());
由于程序员可以告诉编译器一个 int 如何满足input iterator 的概念,你实际上可以在 C++1x 中编写这样的代码,如果你只编写适当的概念图,这真的不是全部这么难。
好的,到此为止。我希望我能告诉你,限制模板并不是那么糟糕,但实际上更好,因为现在编译器知道模板中类型和对它们的操作之间的关系。我什至还没有写过axioms,这是C++1x'概念中的另一个好东西。请记住,这是未来的东西,它还没有推出,但大约会在 2010 年推出。然后我们将不得不等待一些编译器来实现这一切:)
从“未来”更新
C++0x 概念不被草案接受,但在 2009 年底被否决。太糟糕了!但也许我们会在下一个 C++ 版本中再次看到它?让我们充满希望!