一尘不染

是什么使模板与通用模板不同?

java

我了解C 中模板的方面与Java和C#中的泛型不同。C#是一种形式,Java使用类型擦除,C
使用鸭子类型,等等。C模板可以做很多事情,而Java和C#泛型则做不到(例如,模板专业化)。但是 Java泛型可以做很多事情,而C#和C
++则做不到(例如,使泛型族的有界类型参数成为现实class Foo<T extends Comparable<?>>),而

C#泛型可以做的很多事情Java和C
不能做(例如运行时通用反射)。 [编辑:显然Java泛型比我想象的要弱得多。
(这是在说些什么。)无论如何,尽管它们无能为力,但它们仍与C#的泛型一起被视为泛型。]

我不明白的是,概念上使模板与泛型不同的是什么。C
++模板的哪些部分是非模板但不能通用的,无法完成的工作?例如,如果我要实现一种支持模板的语言,则绝对需要包含什么?对于该语言支持泛型,我有什么遗漏呢?

我的猜测是模板是泛型的超集,或者它们是实现泛型的一种方式,但是我真的不了解是什么将真正的模板与真正的泛型区分开。


阅读 348

收藏
2020-12-03

共1个答案

一尘不染

嗯..如果您说您深入理解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是编译时反射的一种形式。

因此,差异世界中最大的参与者被减少为应用修补程序来掩盖运行时的不足,这几乎是完全缺乏运行时自省的一种奇怪的(有益的)副作用:))

因此,我说除了由laguage强制执行的某些任意操作或由运行时平台强制执行的某些任意操作之外,没有其他区别。

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

2020-12-03