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

我理解C ++中与Java和C#中的generics不同的模板方面。 C#是一种物化,Java使用类型擦除,C ++使用duck typing等.C ++模板可以做许多事情,Java和C#generics不能(例如模板专门化)。 但是Javagenerics可以做的事情有很多东西,C#和C ++都做不到(比如制作一个generics系列的有界类型参数,比如class Foo<T extends Comparable> ),还有很多东西C#generics可以做到Java和C ++不能(例如运行时genericsreflection)。 [编辑:显然Javagenerics比我想象的要弱得多。 (这是在说些什么。)无论如何,尽管它们不合适,但它们仍然被认为是仿制药和C#的仿制药。

我不明白的是概念上使模板与generics不同。 C ++模板的哪些部分是不能在不是模板的东西中完成的东西,而是通用的? 例如,如果我要实现支持模板的语言,那么它绝对需要什么呢? 我可以忽略哪些语言支持generics所必需的?

我的猜测是模板是一组超级generics,或者它们是实现generics的一种方式,但我并不真正理解真正模板与真正模板之间的区别。

嗯…如果你说你深入理解C ++模板并且说你没有看到/感觉到generics和它们之间的区别,那么,很可能你是对的:)

有很多不同之处将描述仿制药如何/为什么比模板更好,列出大量的差异等等,但这与这个想法的核心无关。

我们的想法是允许更好的代码重用。 模板/generics为您提供了一种构建某种高阶类定义的方法,这些类定义抽象了一些实际类型。

在这个术语中,它们之间没有区别,唯一的区别是由底层语言和运行时的特定function和约束强制执行的区别。

有人可能会争辩说generics提供了一些额外的function(通常在讨论对象类树的动态内省时),但很少有(如果有的话) 无法在C ++的模板中手动实现。 通过一些努力,大多数都可以实现或模拟,因此它们不能区分“适当的generics”和“真实模板”。

其他人会争辩说,由于C ++的复制粘贴行为,可用的优化潜力很大。 对不起,不是真的。 Java和C#中的JIT也可以做到,差不多,但做得很好。

然而,有一件事可以使Java / C#的generics成为C ++模板function的真正子集。 你甚至提到过它!

它是模板专业化

在C ++中,每个特化都表现为完全不同的定义。

在C ++中,专门用于T == int的template Foo可能如下所示:

 class Foo { void hug_me(); int hugs_count() const; } 

而专用于T == MyNumericType的“相同”模板可能看起来像

 class Foo { void hug_me(); MyNumericType get_value() const; void reset_value() const; } 

仅供参考:那只是伪代码,不会编译:)

Java和C#的generics都不能这样做,因为它们的定义表明所有generics类型实现都将具有相同的“用户界面”。

更重要的是,C ++使用SFINAE规则。 模板可能存在许多“理论上冲突”的特殊化定义。 但是,在使用模板时,只使用那些“实际上好”的模板。

使用类似于上面示例的类,如果您使用:

  Foo foood; foood.reset_value(); 

只会使用第二个特化,因为第一个不能编译,因为…“reset_value”缺失。

使用generics,你不能这样做。 您需要创建一个包含所有可能方法的generics类,然后在运行时动态检查内部对象,并为不可用的方法抛出一些“未实现”或“不支持”的exception。 那……太可怕了。 这样的事情应该可以在编译时完成。

模板专业化SFINAE的实际function,含义,问题和整体复杂性是真正区分generics和模板的因素。 简单地说,generics是以这种方式定义的,专业化是不可能的,因此SFINAE是不可能的,因此,整个机制矛盾地更容易/更简单。

在编译器的内部实现更简单/更简单,并且非智能大脑可以理解。

虽然我同意Java / C#中generics的整体优势,但我真的很想念专业化,界面灵活性和SFINAE规则。 但是,如果我没有提到一个与理智的OO设计相关的重要事情,那就不公平了:如果xxx类型的模板专业化实际上改变了它的客户端API,那么很可能它应该以不同的名称命名并且应该形成不同的模板。 模板可以做的所有额外好处大多都添加到工具集中,因为…在C ++中没有reflection,必须以某种方式进行模拟。 SFINAE是编译时reflection的一种forms。

因此,差异世界中最大的玩家被降低到应用于掩盖运行时缺陷的修补程序的好奇(有益)副作用,这几乎完全缺乏运行时内省:))

因此,我说除了laguage强制执行的一些任意的,或者运行时平台强制执行的任意一些之外没有区别。

所有这些只是高阶类或函数/方法的一种forms,我认为这是最重要的事情和特征。

首先,我发现有趣的是RTTI /内省是大多数答案的重要组成部分。 嗯,这不是generics与模板的区​​别,而是具有instrospection的语言与没有它的语言。 否则你也可以声称C ++类与Java类的区别,以及带有Java函数的C ++函数……

如果你不去考虑,那么主要的区别在于模板定义了一种图灵完整的语言,虽然语法很糟糕,但你可以编程 。 我听说的第一个非常复杂的例子(我希望有代码,但我不喜欢)是一个在编译时计算素数的程序。 这确实带来了另一个区别:模板可以采用类型参数,模板参数或非类型参数( 非类型引用任何不是类型或模板的东西,如int值)。

在其他答案中已经提到过这一点,但只是说模板可以是专用的,并且有SFINAE没有明确说明这两个特征足以生成图灵完整语言。

Javagenerics可以做的事情有很多,C#和C ++都做不到(例如,创建一个generics系列的有界类型参数,如class Foo>

对于这个例子并不完全正确:

 template  struct Foo { static bool compare(const Comparable &lhs, const Comparable &rhs) { return lhs == rhs; } }; 

仅当模板参数是等同性类型时,此类模板才能成功实例化compare函数。 它不被称为“有界类型参数”,但它具有相同的用途。

如果在C ++中你想将Comparable视为显式接口(即基类)而不是duck-typed概念,那么你可以使用static_assert(is_base_of::value, "objects not Comparable"); , 管他呢。

不,模板不是generics的超级集合,使用C ++模板,你没有与C#generics相同级别的运行时支持,这意味着C ++中的RTTI无法检测并提供模板的元数据,如Reflection所做的C#中的generics。

除此之外,我喜欢这个片段:

C ++模板使用编译时模型。 在C ++程序中使用模板时,效果就像使用了复杂的宏处理器一样。

C#generics不仅是编译器的一个特性,也是运行时的一个特性。 诸如List之类的generics类型在编译之后保持其generics(通用性)。 或者,换一种方式来看,C ++编译器在编译时所做的替换是在C#通用世界的JIT时间完成的。

请参阅此处查看完整文章: C#generics如何与C ++模板进行比较?

我将限制我对C ++模板和Javagenerics的回答。

  1. C ++模板(类模板和函数模板)是实现编译时多态性的机制,但AFAIK Javagenerics是运行时机制。
  2. 使用C ++模板,你可以进行generics编程,它实际上是完全独立的编程风格/范例,但Javagenerics本身就是OO风格。 见下文:
  3. C ++模板基于Duck类型,但Javagenerics基于Type Erasure。 在C ++中, vectorvectorvectorvector>是4种不同的类型,但在Java CellCell CellCell是同一类型。 更确切地说,在代码生成期间,编译器首先擦除类型。 您可以按照以下论文中的代码进行检查:Vladimir Batov。 Javagenerics和C ++模板。 C / C ++ Users Journal,2004年7月。

     public class Cell { private E elem; public Cell(E elem) { this.elem = elem; } public E GetElement() { return elem; } public void SetElement(E elem) { this.elem = elem; } } boolean test() { Cell IntCell(10); Cell StrCell(“Hello”); return IntCell.getClass() == StrCell.getClass(); // returns true } 

简而言之,Java假装是通用的,而C ++实际上是。