实现比较运算符的模板类

6

对于我来说,经常的任务之一就是为类编写所有重载比较运算符。因此,我编写了一个模板类,如果派生类实现了“==”和“<”,则该模板类将实现“<”,“<=”,“>=”和“!=”。它可以正常工作,但涉及大量类型转换和不太明显的“奇异递归模板模式”,所以我想知道是否有更简单的解决方案?

template <class Derived>
class Comparable
{
public:

    bool operator!=(const Comparable<Derived>& other) {
        return !(static_cast<Derived*>(this)->operator==
                     (*static_cast<const Derived*>(&other)));
    }

    bool operator<=(const Comparable<Derived>& other) {
        return (static_cast<Derived*>(this)->operator==
                    (*static_cast<const Derived*>(&other)))
                || (static_cast<Derived*>(this)->operator<
                    (*static_cast<const Derived*>(&other)));
    }

    bool operator>(const Comparable<Derived>& other) {
        return !(static_cast<Derived*>(this)->operator==
                    (*static_cast<const Derived*>(&other)))
                && !(static_cast<Derived*>(this)->operator<
                    (*static_cast<const Derived*>(&other)));
    }

    bool operator>=(const Comparable<Derived>& other) {
        return !(static_cast<Derived*>(this)->operator<
                    (*static_cast<const Derived*>(&other)));
    }
};

3
http://www.boost.org/doc/libs/1_58_0/libs/utility/operators.htm - interjay
有的。我会提出与@interjay上面链接的类似解决方案:类内的友元函数定义是可以通过派生类型上的ADL找到的inline自由函数。您可以使用它来提供所需的运算符。 - David Rodríguez - dribeas
1个回答

5

如果从评论中的描述不明显:

template <typename T>
struct Comparable {
   friend bool operator!=(T const & lhs, T const & rhs) { return !(lhs == rhs); }
   friend bool operator> (T const & lhs, T const & rhs) { return   rhs <  lhs;  }
// ...
};
class MyType : Comparable<MyType> {
   int data;
   friend bool operator==(MyType const & lhs, MyType const & rhs) {
      return lhs.data == rhs.data;
   }
   friend bool operator< (MyType const & lhs, MyType const & rhs) {
      return lhs.data <  rhs.data;
   }
  public:
// ...
};

当编译器遇到MyType a, b; a>b;时,查找运算符将最终进行ADL,它将查找MyTypeComparable<MyType>(作为一个基类)中的实现,从而找到您需要的实现:bool operator>(MyType const&, MyType const&)
运算符作为自由函数的好处是允许在比较的类型之外定义该函数(在此情况下是基类),同时仅通过ADL访问这些操作符(两个参数中必须有一个为Comparable<MyType>)。使用自由函数还提供了类型对称性,编译器将允许在两侧进行隐式转换,而在成员函数的情况下,它只会允许右侧操作数进行转换。
为了完整起见,可以通过在命名空间中提供操作符模板以及可用于ADL目的的标签来提供不同的技巧:
namespace operators {
   template <typename T> 
   bool operator>(T const & lhs, T const & rhs) {
       return rhs < lhs;     
   }
// rest of the operators come here
   struct tag {};
}
class MyType : operators::tag {
   int data;
   friend bool operator<(T const & lhs, T const & rhs) {
      return lhs.data < rhs.data;
   }
//...
};

这个技巧本质上与之前的类似,只不过在这种情况下,运算符不是在基类中找到的,而是在与之关联的命名空间中找到的。这个解决方案比之前的方案要稍微逊色一些,因为它容易被误用,包括使用 using namespace operators; 会使得模板运算符对于所有类型都可用。

我认为这不会起作用,因为友元不可继承。 - simon
@simon:这与友谊无关,friend关键字用于允许在类定义内部定义自由函数,但实现仅使用公共接口。 - David Rodríguez - dribeas

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