模板类实现比较运算符

sim*_*mon 6 c++ templates operator-overloading

我将所有重载的比较运算符写入类是一个常见的任务,所以我编写了一个模板类,如果派生类实现了==和<,则实现<,<=,> =,!=.它工作但有很多演员和不那么明显的"奇怪的重复模板模式",所以我想知道是否有更简单的解决方案?

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)));
    }
};
Run Code Online (Sandbox Code Playgroud)

Dav*_*eas 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:
// ...
};
Run Code Online (Sandbox Code Playgroud)

当编译器遇到对MyType a, b; a > b;运算符的查找时,最终会执行 ADL,它将查找内部MyTypeand Comparable<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;
   }
//...
};
Run Code Online (Sandbox Code Playgroud)

技巧基本上是相同的,只不过在这种情况下,运算符不是在基类内部找到的,而是在与其关联的命名空间中找到的。该解决方案比前一个解决方案稍差一些,因为它容易遭受不同形式的误用,包括using namespace operators;使模板化运算符可用于所有类型。