七、模板与泛型编程
C++ template的最初动机 —— 让我们得以建立“类型安全”的容器,如 vector,list 和 map。
而且 泛型编程写出的代码和其所处理的对象类型彼此独立。
C++ template机制自身是一部完整的图灵机:它可以被用来计算任何可计算的值。于是,导出了模板元编程,创造出“在C++编译器内执行并于编译完成时停止执行”的程序。
条款41、了解隐式接口和编译期多态
1. 显示接口 及 运行期多态
面向对象的世界总是以显示接口和运行期多态解决问题。
class Widget {
public:
Widget();
virtual ~Widget();
virtual std::size_t size() const;
virtual void normalize();
void swap(Widget& other);
...
};
void doProcessing(Widget& w)
{
if( w.size() > 10 && w != someNastyWidget ) {
Widget temp(w);
temp.normalize();
temp.swap(w);
}
}
我们可以说doProcessing的w:
- 由于w的类型被声明为Widget,所以w必须支持Widget接口,我们可以在源码中找出接口(在.h文件中),所以它在源码中明确可见。
- 由于Widget的某些成员函数是virtual的,所以将在运行期根据w的动态类型决定调用哪个函数,即 w对那些函数的调用表现出运行期多态。
2. 隐式接口 及 编译器多态
将上述例子中的 doProcessing函数 转变为 函数模板
template<typename T>
void doProcessing(T& w)
{
if( w.size() > 10 && w != someNastyWidget ) {
T temp(w);
temp.normalize();
temp.swap(w);
}
}
再来说说doProcessing里的w
- w必须支持哪一种接口,由template中执行于w身上的操作来决定。本例中,w的类型T必须支持size,normalize 和 swap成员函数、copy构造函数、不等比较等。
- 只要涉及w的任何函数调用,比如 operator > 与 !=,有可能造成template具现化,这样的具现行为发生在编译器。
“以不同的template参数具现化function templates”会导致调用不同的函数,这便是所谓的编译期多态。
3. 编译期多态 与 运行期多态 的 差异 and 显式接口 与 隐式接口 的 差异
编译期多态 与 运行期多态 的差异类似于 哪一个重载函数应该被调用(发生在编译期) 与 哪一个virtual函数该被绑定(发生在运行期)。
显式接口 与 隐式接口 的差异可以通过例子来讲述:
通常 显式接口由函数的签名式(函数名称、参数类型、返回类型)构成,比如Widget类的 构造函数、析构函数、size、normalize、swap函数 及其 参数类型、返回类型、常量性,还有 编译器产生的 copy构造函数 与 copy assignment操作符。
此外,也可以包括 typedefsclass Widget { public: Widget(); virtual ~Widget(); virtual std::size_t size() const; virtual void normalize(); void swap(Widget& other); };
隐式接口就不同于此,并不基于函数签名式,而是由有效表达式组成。
template<typename T> void doProcessing(T& w) { if( w.size() > 10 && w != someNastyWidget ) { ... } }
T的隐式接口有这些约束
- 必须提供一个名为size的成员函数,该函数返回一个整数值。
- 必须支持一个 operator != 函数,用来比较两个T对象(此处假设 someNastyWidget类型为T)
隐式接口仅仅是由一组有效表达式构成,表达式自身可能看起来复杂,但它们要求的约束条件一般简单又明确。
在 template 参数身上的隐式接口,就像加在 class 对象身上的显式接口,两者都在编译期完成检查。
4. 请记住
- class 和 template 都支持接口 和 多态
- 对 class 而言接口是 显示的(explicit),以函数签名为中心;多态则是通过 virtual函数 发生于 运行期
- 对 template 参数而言,接口是 隐式的(implicit),奠基于有效表达式;多态则是通过template具现化 和 函数重载解析 发生于编译期