如何最好地重载运算符< > < => =但只写一个或两个比较函数?

时间:2012-12-17 16:17:51

标签: c++ comparison operator-overloading

我有一个类型为std :: string的类。我想通过比较属性为类提供一些比较运算符函数,例如&lt ;,>,==,< =和> =。

我的问题是:任何简单的方法或工具

(1)只写一个或两个函数,例如一个用于运算符< (和==),其他人可以自动生成。

(2)甚至更简单,因为类比较取决于已经提供了比较函数的类型std::string的属性。

4 个答案:

答案 0 :(得分:25)

奇怪的重复模板模式

在这种情况下,您提供了一个简单的基类,它实现了所有需要的运算符,并且只是从它继承:

template <class T>
struct implement_relational_operators{
    friend bool operator<=(const T & a,const T & b){ return a < b || a == b; }
    friend bool operator>(const T &  a, const T & b){ return !(a <= b); }
    friend bool operator!=(const T &  a, const T & b){ return !(a == b);}
    friend bool operator>=(const T &  a, const T & b){ return !(a < b); }
};

template <class T>
struct scalar : public implement_relational_operators<scalar<T> >{
    T value;
    bool operator<(const scalar& o) const { return value < o.value;}
    bool operator==(const scalar& o) const { return value == o.value;}
};

这并不存在std::rel_ops的缺点(见下文)。但是,您仍需要实施operator<operator==boost使用了类似的技巧。

C ++ 20 <=>可能会进一步改善这种情况(见下文)。

std::rel_ops(在C ++ 20中弃用)

std::rel_ops提供基于<==的其他操作,因此您只需要编写两个运算符。

但是,它将在C ++ 20中弃用,其中a <=> b将被混合使用。

要求

您只需要等于(operator=)和小于(operator<)比较运算符。其余部分可以使用std::rel_ops自动生成,因为以下内容成立:

a != b equal to !(a == b)
a <= b equal to (a < b) || (a == b)
a >= b equal to !(a < b)
a >  b equal to !(a <= b)

请注意,这些效率低于手动编写它们的效率。

警告

但是,由于自己很容易为这些操作员提供服务,因此您应该花费额外的精力并编写它们。它们也有一些缺点,如R. Martinho Fernandes所述:

  

请注意,除非您:[/ p>,否则[他们]将无法正常工作      

      
  1. 在您使用运算符的每个上下文中添加using namespace std::rel_ops;或
  2.   
  3. 在您的类的命名空间中添加using::operator!=; using::operator<=; using::operator>=; using::operator>;(在您的类的命名空间中using namespace std::rel_ops是不可接受的,因为ADL无法获取它。)
  4.   

答案 1 :(得分:8)

你可以使用std::rel_ops,但这是错误的。这是一个不属于任何人工具箱的大锤。

通常的方法是定义operator==operator<,并了解班级的详细信息。剩下的四个运算符可以写成:

a != b!(a == b)

a <= b!(b < a)

a > bb < a

a >= b!(a < b)

答案 2 :(得分:5)

一种选择是使用CRTP,如Barton Nackman技巧:

https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

https://en.wikipedia.org/wiki/Barton%E2%80%93Nackman_trick

// A class template to express an equality comparison interface.
template<typename T> class equal_comparable {
    friend bool operator==(T const &a, T const &b) { return  a.equal_to(b); }
    friend bool operator!=(T const &a, T const &b) { return !a.equal_to(b); }
};

class value_type
 // Class value_type wants to have == and !=, so it derives from
 // equal_comparable with itself as argument (which is the CRTP).
 : private equal_comparable<value_type> {
  public:
    bool equal_to(value_type const& rhs) const; // to be defined
};

答案 3 :(得分:2)

(如果您可以控制班级)正确的方法是使用Boost.Operators http://www.boost.org/doc/libs/1_66_0/libs/utility/operators.htm

#include<cassert>
#include<boost/operators.hpp>

struct A : boost::totally_ordered<A> // implies equality-comparable and less-than-comparable
{
    int val_;
    A(int const& v) : val_{v}{}
    bool operator==(A const& other) const{return other.val_ == val_;}
    bool operator<(A const& other) const{return other.val_ < val_;}
};

int main(){
    A a1{5};
    A a2{7};

    assert(!(a1 == a2)); // user defined operator==
    assert(a1 != a2);    // automatically defined !=
    assert(a1 < a2);     // user defined operator<
    assert(a2 > a1);     // automatically defined >
    assert(a2 >= a1);    // automatically defined >=
    assert(a1 <= a2);    // automatically defined <=
}

Boost.Operators使用CRTP(参见其他答案),但为你做了一切。

我刚刚从其他关于std::rel_ops的答案中学到了什么,但是我不成熟的结论是它不是很有用。 Boost.Operators似乎更强大和可定制,虽然它有点干扰。

请注意,您必须知道要实施哪些操作(在这种情况下为operator==operator<。但在原则中,您可以只需定义operator!=operator>。 其他运营商自动“推断”会很酷,请参阅Very automatic operator generator in C++

这是另一个例子,只有一个操作员就够了。 (但可能效率低下?)

#include<boost/operators.hpp>

struct A : boost::totally_ordered<A>, boost::equivalent<A>{
    int val_;
    A(int const& v) : val_{v}{}
    bool operator<(A const& other) const{return other.val_ < val_;}
};