嗯..如果你说你深入了解C++模板,并说你没有看到/感觉到泛型和它们之间的区别,那么,很可能你是对的:)
有许多差异可以描述泛型如何/为什么比模板更好,列出大量的差异等,但这与这个想法的核心无关。
这个想法是允许更好的代码重用。模板/泛型为您提供了一种构建某种高阶类定义的方法,这些定义抽象于某些实际类型。
在这种情况下,它们之间没有区别,唯一的区别是由基础语言和运行时的特定功能和约束强制执行的。
有人可能会争辩说,泛型提供了一些额外的功能(通常是在谈论对象类树的动态内省时),但其中很少有(如果有的话)不能在C++的模板中手动实现。通过一些努力,它们中的大多数都可以实现或模拟,因此它们不能很好地区分“适当的泛型”和“真正的模板”。
其他人会争辩说,由于C++的复制粘贴行为而可用的优化的潜在潜力就是区别。抱歉,事实并非如此。Java和C#中的JIT也可以做到这一点,嗯,差不多,但做得很好。
然而,有一件事确实可以使Java / C#的泛型成为C++模板功能的真正子集。你甚至提到了它!
它是模板专用化。
在C++中,每个特化都表现为一个完全不同的定义。
在C++中,专门针对 T==int 可能如下所示:template<typename T> Foo
class Foo<int>
{
void hug_me();
int hugs_count() const;
}
而专门用于T==MyNumericType的“相同”模板可能看起来像
class Foo<MyNumericType>
{
void hug_me();
MyNumericType get_value() const;
void reset_value() const;
}
仅供参考:这只是伪代码,不会编译:)
Java和C#的泛型都不能做到这一点,因为它们的定义指出,所有泛型类型具体化将具有相同的“用户界面”。
更重要的是,C++使用了SFINAE规则。对于模板,可能存在许多“理论上碰撞”的特化定义。但是,当使用模板时,仅使用那些“实际良好”的模板。
使用与上述示例类似的类,如果您使用:
Foo<double> foood;
foood.reset_value();
只会使用第二个特化,因为第一个特化不会编译,因为...“reset_value”缺失。
使用泛型,您无法做到这一点。您需要创建一个具有所有可能方法的泛型类,然后在运行时动态检查内部对象,并为不可用的方法引发一些“未实现”或“不支持”的异常。那是。。。太可怕了。这样的事情在编译时应该是可能的。
模板专业化和 SFINAE 的实际功能、含义、问题和整体复杂性是真正区分泛型和模板的原因。简单地说,泛型是这样定义的,即专业化是不可能的,因此SFINAE是不可能的,因此,整个机制自相矛盾地更容易/更简单。
既更容易/更简单地在编译器的内部实现,也可以被非学者的大脑理解。
虽然我同意 Java/C# 中泛型的整体优势,但我真的很想念专用化、接口灵活性和 SFINAE 规则。但是,如果我不提及与合理的OO设计相关的一件重要事情,那将是不公平的:如果你xxx类型的模板专用化实际上改变了它的客户端API,那么很可能它应该以不同的方式命名,并且应该形成一个不同的模板。模板可以做的所有额外好东西大多被添加到工具集中,因为......在C++中没有反射,必须以某种方式模仿它。SFINAE 是编译时反射的一种形式。
因此,差异世界中最大的参与者被简化为一个奇怪的(有益的)副作用,即应用一个修补程序来掩盖运行时的缺陷,即几乎完全缺乏运行时内省:))
因此,我说除了一些由laguage强制执行的任意区别或由运行时平台强制执行的一些任意区别之外,没有其他区别。
它们都只是高阶类或函数/方法的一种形式,我认为这是最重要的事情和特性。