模板与通用模板有何不同?

2022-09-02 10:33:39

我了解C++模板的各个方面,这些方面与Java和C#中的泛型不同。C#是一种重新化,Java使用类型擦除,C++使用鸭子类型等。C++模板可以做许多Java和C#泛型无法做到的事情(例如,模板专用化)。但是,Java 泛型可以做很多 C# 和 C++ 做不到的事情(例如,制作一个泛型家族的有界类型参数,如类 Foo<T extendsable<?>>),以及 C# 泛型可以做许多 Java 和 C++ 做不到的事情(例如运行时泛型反射)。[编辑:显然Java泛型比我想象的要弱得多。(这是在说些什么。无论如何,尽管它们无能,但它们仍然与C#的泛型一起被认为是泛型。

我不明白的是,在概念上,模板与泛型有什么不同。C++模板的哪些部分是无法在不是模板但通用的事物中完成的事情?例如,如果我要实现一种支持模板的语言,那么其中绝对需要什么?我可以省略哪些语言支持泛型所必需的内容?

我的猜测是,模板是泛型的超集,或者它们是实现泛型的一种方式,但我真的不明白真正的模板与真正的泛型有什么区别。


答案 1

嗯..如果你说你深入了解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强制执行的任意区别或由运行时平台强制执行的一些任意区别之外,没有其他区别。

它们都只是高阶类或函数/方法的一种形式,我认为这是最重要的事情和特性。


答案 2

首先,我觉得有趣的是,RTTI/内省是大多数答案的重要组成部分。好吧,这不是泛型与模板的区别,而是具有介绍性的语言与没有它的语言。否则,你也可以声称是C++类与Java类的区别,以及C++函数与Java函数的区别......

如果你把介绍排除在外,主要区别在于模板定义了一种图灵完备语言,它在风格上是功能性的,尽管它有一个可怕的语法,你可以在上面编程。我听说过的第一个非常复杂的例子(我很想有代码,但我没有)是一个在编译时计算素数的程序。这确实带来了另一个区别:模板可以采用类型参数,或模板参数或非类型参数(非类型是指任何不是类型或模板的东西,如值)。int

这在其他答案中已经提到过,但仅仅说模板可以是专门的,并且存在SFINAE并没有明确说明这两个功能足以生成图灵完备语言。