Template与泛型的世界,与面向对象有根本上的不同。在此世界中显式接口和运行期多态仍然存在,但重要性降低。反倒是隐式接口和编译期多态变得重要。看看下面的函数模板:
template <typename T>
void doProcessing(T& w) {
if (w.size() > 10 && w != someNastyWidget) {
T temp(w);
temp.normalize();
temp.swap(w);
}
}
- w必须支持一种接口,由template中执行于w身上的操作来决定。本例看来w的类型T必须支持size, normalize和swap成员函数,copy构造函数,比较运算符。这一组表达式便是T必须支持的一组隐式接口。
- 凡涉及w的任何函数调用,有可能造成template的实例化,使这些调用得以成功。这样的实例化行为发生在编译期。以不同的template参数实例化函数模板会导致调用不同的函数,这就是所谓编译期多态。
通常显式接口由函数的签名构成,例如Widget class:
class Widget {
public:
Widget();
virtual ~Widget();
virtual std::size_t size() const;
virtual void normalize();
void swap(Widget& other);
};
隐式接口就完全不同了,它并不基于函数签名,而是由有效表达式组成。上述函数模板中,T的隐式接口好像有这些约束:
- 它必须提供一个名为size的成员函数,该函数返回一个整数。
- 它必须支持
operator!=
,用来比较两个T对象。这里我们假设someNastyWidget的类型为T。
但是由于运算符重载带来的可能性,这两个约束都不需要满足。T必须支持size成员函数,但是这个函数不需要返回一个整数值,只需要返回一个类型为X的对象,这个对象必须和10支持 operator>
。其他运算符重载同理。
请记住
- class和template都支持接口和多态。
- 对class而言接口都是显式的,基于函数签名。多态则是通过virtual函数发生于运行期。
- 对template参数而言,接口是隐式的,基于有效表达式。多态则是通过template实例化和函数重载决议发生发于编译期。