模板和多态之间有什么区别?

20

大家好,我对模板和多态性有一个疑问。按照定义,多态提供代码的可重用性,而模板在某种程度上允许用户使用相同的代码,通过提供不同的数据类型进行泛型编程。那么,使用多态性比使用模板有什么好处呢?这可能是一个愚蠢的问题,但我很想知道确切的区别。


1
模板提供静态/编译时多态性。而虚拟化提供动态多态性。 - Alok Save
1
有什么不同?一切都不同。多态甚至与代码可重用性毫不相关,而模板则是。 - Mooing Duck
2
@MooingDuck:多态确实允许代码重用。旧代码(针对多态基类编写)可以使用新代码(针对同一基类编写)。 - Nicol Bolas
1
@Nicol:我认为他的观点是多态性可能允许代码重用,但这并不是它的实际目的。 - ildjarn
显然,他们正在询问如何最好地解决他们遇到的某个问题。在这种情况下,他们没有提供足够的有关问题的信息。有些问题最好使用模板来解决,而其他问题则需要使用多态性。 - D Left Adjoint to U
2个回答

30

你好像误解了什么是多态。

多态的核心与派生类无关。多态只是指能够在不知道类型的情况下使用该类型的能力。多态不使用具体类型,而是依赖某种原型来定义它所接受的类型。符合该原型的任何类型都被接受。

C++ 中提供的运行时多态性是通过从包含虚函数的基类派生类来实现的。基类和虚函数构成了多态原型。编写为调用这些虚函数并接受基类的代码将接受从基类派生的 任何 类实例。

编译时 多态是发生在...编译时的多态;) 这意味着编译器必须知道正在发生的事情。你可能已经针对多态原型编写了 C++ 代码,但编译器并不关心。你将在编译后获得具体的特定类型。

C++中通过模板实现编译时多态。模板函数或类可以采用符合原型(通常称为“概念”)的任何类型。与基类和虚函数不同,原型是隐式的:原型仅由模板函数/类如何通过该类型使用来定义。

如果你有这个模板函数:

template<typename T>
void Stuff(T &t)
{
  t.call(15);
}

对于 T,存在一个隐含的要求。这个要求是它必须有一个名为 call 的成员函数,并且在调用时可以传入一个整数值。

这意味着任何符合这个原型的类型都可以被使用。

模板多态性比继承多态性更加广泛,因为它可以被更广泛的类型使用。一个类型必须专门设计为使用继承多态性;你必须从一个类派生。一个类型可以被非破坏性地(例如:无需改变类型本身)适应于模板多态性。如果你的模板原型设计得很好,那么更是如此:

template<typename T>
void Stuff(T &t)
{
  call(t, 15);
}

这个版本的Stuff只需要有一些函数接受一个T&和一个整数值。如果我想要使用Stuff来处理某种类型,我只需要在适当的命名空间(即定义该类型的命名空间)中定义一个call函数。这将完美地工作,而无需修改类型本身。
当然,编译时多态是...编译时的。如果我想让用户输入或数据文件选择多态类型,模板并不能提供太多帮助(虽然基于模板的类型擦除可以帮助)。运行时多态的主要好处是它确实是运行时的。
另一个好处是它对其原型更加精确。关于继承,所有内容都明确说明了。基类中的虚函数接口被清楚地列出。编译器会阻止您尝试不正确地使用该基类(调用不存在于其中的方法)。事实上,一个不错的IDE将指导您的代码,以便您只能看到基类上的方法。
模板多态性要隐式得多。由于C++没有办法拼写出特定模板函数/类对类型产生的原型,所以很容易意外调用模板类型上不应该调用的东西。只有当您尝试使用不符合原型的类型时,编译器才会检测到这一点。即使如此,您通常也会得到一个大量错误信息的输出(取决于您的模板代码嵌套有多深),这使得很难知道问题出在哪里。
实现隐式模板多态原型也要困难得多,因为它没有被明确说明。实现派生类需要遍历基类,查看所有虚函数,并实现它们。对于模板原型来说,这样做要困难得多,除非有某个地方有详细说明。如果您未能实现某些东西,您将再次得到一个不太清楚问题所在的错误信息输出。

1
这个成员函数必须有一个单一的重载版本,可以使用整数值进行调用,并且该调用与int不应该是模糊的,可能还有其他我忘记的条件。这就是为什么多态原型通常以“此表达式必须具有以下属性:…”的术语记录的原因。如果您尝试根据存在哪些函数和重载来记录它,则会涉及各种边缘情况。 - Steve Jessop
他们知道什么是多态性/模板。显然,您可以选择使用模板来执行操作,这意味着您的应用程序需要生成C++代码并重新编译,因此需要与编译器捆绑在一起(取决于您要做什么)。使用多态性,一切都可以在运行时完成,无需重新启动或重新编译应用程序。两者之间确实存在相似之处。我正在考虑是应该使用模板还是使用构造函数参数(以多态方式)。我想,由于我希望扩展以C++编码,所以我会使用模板。 - D Left Adjoint to U
另一方面,如果配置某些东西的方式数量是无限的,那么多态性可能是最好的选择。以 Category<ObjectType, MorphismType>Category(Objects, Morphisms) 为例。我认为,由于您可以将任何有限集合作为对象集合,因此我应该使用多态性来编写代码,并仅在它们可以减少编码负担的情况下使用模板。 - D Left Adjoint to U
1
@Prime:“他们知道什么是多态/模板。” 我发帖的整个意图就是“多态”也包括某些使用模板的情况。虽然它不是运行时多态,但它只是多态的不同形式。 - Nicol Bolas

3
简而言之,它取决于对要解决问题的共性和变异性分析的结果。
如果您在不同的东西上执行相同的操作,则应使用模板,例如 List<int>List<float>
如果您根据上下文以不同的方式在同一对象上执行相同的操作,并且上下文还具有某些共性,则应使用多态性,例如使用 AbstractInterface::openFile 接口及其派生类 WindowsInterface::openFileLinuxInterface::openFile。根据上下文使用其中之一。抽象接口确定了概念上执行相同操作的共性,派生类满足了实现概念变量性的需求。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接