嗯..如果您说您深入了解 C++ 模板并说您没有看到/感觉泛型与它们之间的区别,那么您很可能是对的 :)
有许多差异将描述泛型如何/为什么比模板更好,列出大量差异等,但这与想法的核心几乎无关。
这个想法是允许更好的代码重用。模板/泛型为您提供了一种构建某种高阶类定义的方法,这些定义抽象了一些实际类型。
在这个术语中,它们之间没有区别,唯一的区别是由底层语言和运行时的特定功能和约束强制执行的区别。
有人可能会争辩说,泛型提供了一些额外的特性(通常在谈论对象类树的动态自省时),但其中很少有(如果有的话)不能在 C++ 的模板中手动实现。通过一些努力,它们中的大多数都可以实现或模拟,因此它们不能很好地区分“适当的泛型”和“真实模板”。
其他人会争辩说,由于 C++ 的复制粘贴行为,优化的巨大潜力是不同的。对不起,不是真的。Java 和 C# 中的 JIT 也可以做到这一点,嗯,几乎,但做得很好。
然而,有一件事确实可以使 Java/C# 的泛型成为 C++ 模板功能的真正子集。你甚至提到了它!
它是模板专业化。
在 C++ 中,每个特化都表现为完全不同的定义。
在 C++ 中,template<typename T> Foo
专用于 T==int 可能如下所示:
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 是编译时反射的一种形式。
因此,差异世界中最大的参与者被简化为用于掩盖运行时缺陷的修补程序的奇怪(有益)副作用,即几乎完全缺乏运行时内省:))
因此,我说除了语言强制执行的一些任意的,或者运行时平台强制执行的一些任意的之外,没有区别。
它们都只是高阶类或函数/方法的一种形式,我认为这是最重要的东西和特性。