目的
在不依赖名字空间或函数模板重载决议的前提下支持运算符重载。
别称
技巧的发明者最初将其称为限定的模板展开(Restricted Template Expansion),但是这个术语从来没有被广泛使用。
动机
1994年,John Barton和Lee Nackman为了解决当时C++实现上的限制,首次发表了这个惯用法。虽然现在此惯用法的原始用途已经不再必须,现行的标准仍然保留了对它的支持。
在John Barton和Lee Nackman发明这个惯用法的时期,C++不支持函数模板的重载,而且很多C++实现尚不支持名字空间。当需要为类模板定义运算符重载时,这会导致一些问题。考虑下面的类:
template<typename T> class List { // ... };
要定义一个相等运算符,最自然的想法是在名字空间范围内定义一个非成员函数(由于那个时期的编译器不支持名字空间,所以在全局范围内定义)。将==运算符定义为非成员函数意味着函数的两个参数会被对称的处理,其中一个参数为这个对象的this指针的情况不会出现。这样一个相等运算符可能看起来像这样:
template<typename T> bool operator==(List<T> const & lft, List<T> const & rgt) { //... }
然而,在那时函数模板是不能被重载的,而且将函数放置在自己的名字空间的做法不能在所有平台上通用。这意味着只有一个类能有这样一个相等运算符。如果有第二个类型要使用,将会导致歧义。
解决方案与示例代码
解决方案通过在类的内部定义一个运算符并声明为友元函数工作:
template<typename T> class List { public: friend bool operator==(const List<T> & lft, const List<T> & rgt) { // ... } };
对模板的实例化现在导致一个非模板函数被注入到全局范围,它的参数已经是具体的,固定的类型。(译注:标准规定函数可以在一个类的友元声明中被定义,这种情况下该函数是类所在名字空间的一个函数,而不是类的成员函数)与任何其他的非模板函数一样,这个模板函数可以被函数重载决议所选择。
该实现可以通过将友元函数作为基类的一部分并通过奇异模板递归模式(Curiously Recurring Template Pattern)继承来达到更加普遍的效果。
template<typename T> class EqualityComparable { public: friend bool operator==(const T & lft, const T & rgt) { return lft.equalTo(rgt); } friend bool operator!=(const T & lft, const T & rgt) { return !lft.equalTo(rgt); } }; class ValueType : private EqualityComparable<ValueType> { public: bool equalTo(const ValueType & other) const; };
已知应用
相关惯用法
Curiously Recurring Template Pattern
参考资料
Barton-Nackman trick on Wikipedia
原文链接
http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Barton-Nackman_trick