正如 Bjarne Stroustrup 所指出的,“C++ 是一门多范式语言。”它支持许多不同的编程风格(范式),而面向对象编程只是其中之一。其他的还有结构化编程和泛型编程等。多年来,Andrei Alexandrescu、Scott Meyers 和 Herb Sutter 等 C++ 专家一直在推广泛型编程的使用,这通常被称为“现代 C++ 设计”。
下面是 Andrei Alexandrescu 对《现代 C++ 设计》的论述:
Modern C++ Design defines and systematically uses 泛型组件 - highly flexible design artifacts that are mixable and matchable to obtain rich behaviors with a small, orthogonal body of code.他的观点中有三个方面特别有趣:
- 《现代 C++ 设计》定义并系统化地使用泛型组件所解释的。
- 高度灵活的设计。
- 用一套小巧、正交的代码体获得丰富的行为。
另一方面,OOP 非常流行:继承和 RTTI 是设计 C++ 应用程序的两个强大机制,许多开发者相比泛型编程方法更偏爱这一范式。
下面是继承的一个常见定义:
In object-oriented programming (OOP), 继承 is when an object or class is based on another object (prototypal inheritance) or class (class-based inheritance), using the same implementation (inheriting from an object or class) specifying implementation to maintain the same behavior (realizing an interface; inheriting behavior). It is a mechanism for code reuse and to allow independent extensions of the original software via public classes and interfaces.许多 C++ 专家建议避免过度使用继承和动态多态。说到底,继承有什么问题呢?
简要的回答:极高的耦合所解释的。
让我们以一个税额计算类的实现为例。

CTaxCalculator 只与继承自 ICalculator 的类协作,无法使用任何其他非 ICalculator 的类——即使那个类也能帮助计算税额。计算器实现类将与 ICalculator 高度耦合,除非引入额外的类和接口来绕过这个限制,否则无法使用其他种类的类。例如,适配器模式(adapter pattern)就是绕开继承造成的紧耦合的一种解决方案。想一想:GoF 的某些设计模式之所以存在,正是为了解决继承引入的紧耦合问题。
泛型编程来救场
使用泛型编程,同样的税额计算器可以这样实现:

相比之下,CGenericTaxCalculator 类可以使用任何能够计算税额的类型来完成计算,而无需知道它的具体类型。重要的是类型所实现的方法,而不是它属于哪个具体的类。这正是泛型编程更自然、更灵活的原因。确实,第一种实现就像现实世界中的这种情形:一家招聘开发者的公司只接受某所特定学校的毕业生,拒绝所有其他人——即使他们具备所需的技能。但这种灵活性是有代价的:代码可能变得更难理解。在 OOP 中,我只需查看 ICalculator 的定义,就能知道对这个类型的要求。然而,使用泛型编程方法时,很难确切知道对模板参数的要求:它必须包含哪些成员?必须满足哪些约束?
C++20 概念(concepts)来救场
下面是 C++20 概念的简要说明:
概念(Concepts) are an extension to C++'s 模板, published as an ISO Technical Specification ISO/IEC TS 19217:2015.[1] They are named boolean predicates on template parameters, evaluated at compile time. A concept may be associated with a template (class template, function template, or member function of a class template), in which case it serves as a 约束: it limits the set of arguments that are accepted as template parameters.概念特性曾多次被推迟。好消息是,C++20 将包含这个有趣的特性。
在这份有趣的文档中,我们可以找到概念特性背后的动机:
The intent of concepts is to model semantic categories (Number, Range, RegularFunction) rather than syntactic restrictions (HasPlus, Array). According to ISO C++ 核心指南 T.20, "The ability to specify a meaningful semantics is a defining characteristic of a true concept, as opposed to a syntactic constraint."有了概念,我们可以解决约束声明的问题,让代码更可读、更易维护。诚然,Boost 多年来一直提供概念(Concepts)的实现,但把它加入语言本身,会让 C++ 更强大、更独特。
泛型编程另一个众所周知的缺点是它的错误信息。有时我们无法轻松理解编译器为什么报错,可能要浪费时间弄清楚代码为什么不像预期那样工作。
幸运的是,概念特性也将改善错误信息,正如这里所解释的。
小结
如果选择 OOP 方法时过度使用继承,就会在类之间引入高耦合,并迫使您仅仅为了解决这个问题而添加更多的类——而选择泛型编程方法就不会如此。幸运的是,实现可读、易维护、低耦合代码所缺失的那块拼图,很快就要成为语言的一部分了。
