我可以使用operator ==如果我只实现了运算符<! - ?

时间:2017-05-23 08:11:21

标签: c++ operator-overloading

我已经为某个对象实现了operator<。 从逻辑上讲,如果!(a < b)!(b < a)表示a == b

这是自动推断的吗?如果我只实施==,我可以使用<吗?

13 个答案:

答案 0 :(得分:69)

由于以下几个原因,C ++无法自动推断出这一点:

  1. 将每种类型与operator<进行比较没有意义,因此类型可能不一定定义operator<
    • 这意味着无法根据operator==
    • 自动定义operator<
  2. operator<不需要比较它的参数。程序员可以为其类型定义运算符,以便为其参数执行几乎任何
    • 这意味着,假设定义了这些运算符,您关于!(a < b) && !(b < a)等同于a == b的陈述可能不一定正确。
  3. 如果您想为您的类型设置operator==功能,请自行定义一个。这并不难:)

    // For comparing, something like this is used
    bool operator==(const MyType& lhs, const MyType& rhs)
    {
        // compare (or do other things!) however you want
    }
    
    // ... though it's not the only thing you can do
    //  - The return type can be customised
    //  - ... as can both of the arguments
    const MyType& operator==(int* lhs, const MyType* const rhs)
    {
        return lhs;
    }
    

答案 1 :(得分:49)

它无法从==推断<因为并非所有类型都是有序的,例如std::complex。是2 + 3i > 1 + 4i还是没有?

此外,即使在通常订购的类型中,您仍然无法推断><的相等性,例如IEEE-754 NaN

double n = std::numeric_limits<double>::quiet_NaN();

std::cout << "NaN == NaN: " << (n == n) << '\n';
std::cout << "NaN < NaN: " << (n < n) << '\n';
std::cout << "NaN > NaN: " << (n > n) << '\n';
std::cout << "NaN != NaN: " << (n != n) << '\n';

除了最后一个

之外,他们都会返回false

答案 2 :(得分:18)

没有。此方法适用于名为totally ordered的类似数字的对象。对于各种类型/类,没有人能保证这种关系。即使没有人能保证operator <能比较一些东西。

所以==只不过是==。您可以通过==实施<,但这并不适合所有人,C ++标准也不适合您。

答案 3 :(得分:12)

C ++不会自动推断出这一点。对于operator>operator<=operator>=,您可以使用std::rel_ops;这只需要operator<。但是,就operator==而言,它不提供operator<。你可以这样做:

template <class T>
bool operator==(T const& lhs, T const& rhs)
{
    return !((lhs < rhs) or (rhs < lhs));
}

请注意:!((lhs < rhs) or (rhs < lhs))!(lhs < rhs) and !(rhs < lhs)在数学上是等效的。

答案 4 :(得分:12)

  

逻辑上,如果!(a&lt; b)和!(b&lt; a),则表示a == b。 c ++是否推断   这个自动?如果我只实现了,我可以使用==

用数学术语表达其他人的陈述:假设你有operator <返回bool并定义严格弱序,并实施{{1返回operator ==,然后此运算符定义与给定的严格弱顺序一致的等价关系。但是,C ++既不需要!(a < b) && !(b < a)来定义严格的弱顺序,也不需要operator <来定义等价关系(尽管许多标准算法如operator ==)可能隐含地使用这些运算符并且需要严格的弱命令rsp。等价关系)。

如果您想根据sort的严格弱序定义所有其他关系运算符,Boost.Operators可能会为您节省一些输入。

因为滥用不符合标准算法要求的operator <非常容易,例如通过operator <std::sort等意外使用它,我建议将std::lower_bound定义为严格的弱订单或根本不定义。 CodesInChaos给出的示例是一个部分顺序,它不符合严格弱顺序的“无法比较的传递性”要求。因此,我建议用不同的名称来调用这种关系,例如operator <

来源:

答案 5 :(得分:6)

编译器不会从==推断<

您可以通过一个简单的示例来检查:

#include <iostream>

struct A {
    A(int r):i{r}{}
    int i;
};

bool operator<(A const & a1, A const& a2) {
    return a1.i < a2.i;
}

int main(int argc, char* argv[]) {
    A a1{2};
    A a2{3};
    if(a1 == a2) {
        std::cout << "equals\n";
    }
    return 0;
}

GCC给出了这个错误:

main.cpp:20:11: error: no match for 'operator==' (operand types are 'A' and 'A')

     if(a1 == a2) {

答案 6 :(得分:6)

std::rel_ops命名空间中定义了自动定义缺失运算符的模板。

它没有根据您的意愿定义基于less运算符的相等运算符。

这仍然非常有用;如果定义less运算符和相等运算符,则可以免费使用其他比较运算符。

答案 7 :(得分:6)

正如许多人所说,不,你不能,编译器不应该这样做。

这并不意味着它不应该 <转到==以及整个无数。

boost::operators试图让事情变得简单。使用它并完成。

如果您想自己动手,只需要一些代码就可以重新实现提升你的功能:

namespace utility {
  namespace details {
    template<class...>using void_t=void;
    template<template<class...>class Z, class, class...Ts>
    struct can_apply:std::false_type{};
    template<template<class...>class Z, class...Ts>
    struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type{};
  }
  template<template<class...>class Z, class...Ts>
  using can_apply = ::utility::details::can_apply<Z,void,Ts...>;
}

namespace auto_operators {
  template<class T, class U>
  using less_r = decltype( std::declval<T const&>() < std::declval<U const&>() );
  template<class T, class U>
  using can_less = ::utility::can_apply<less_r, T, U>;

  struct order_from_less {
    template<class T, class U>
    using enabled = std::enable_if_t<
      std::is_base_of<order_from_less, T>{}
      && std::is_base_of<order_from_less, U>{}
      && can_less<T, U>{},
      bool
    >;
    template<class T, class U>
    friend enabled<U,T>
    operator>(T const& lhs, U const& rhs) {
      return rhs < lhs;
    }
    template<class T, class U>
    friend enabled<U,T>
    operator<=(T const& lhs, U const& rhs) {
      return !(lhs > rhs);
    }
    template<class T, class U>
    friend enabled<T,U>
    operator>=(T const& lhs, U const& rhs) {
      return !(lhs < rhs);
    }
  };
  struct equal_from_less:order_from_less {
    template<class T, class U>
    using enabled = std::enable_if_t<
      std::is_base_of<order_from_less, T>{}
      && std::is_base_of<order_from_less, U>{}
      && can_less<T, U>{} && can_less<U,T>{},
      bool
    >;
    template<class T, class U>
    friend enabled<U,T>
    operator==(T const& lhs, U const& rhs) {
      return !(lhs < rhs) && !(rhs < lhs);
    }
    template<class T, class U>
    friend enabled<U,T>
    operator!=(T const& lhs, U const& rhs) {
      return !(lhs==rhs);
    }
  };
}

上述内容只需写一次,或等同于#include提升。

一旦你有了提升,或者上面的提升,它就像下面这样简单:

struct foo : auto_operators::equal_from_less {
  int x;
  foo( int in ):x(in) {}
  friend bool operator<( foo const& lhs, foo const& rhs ) {
    return lhs.x < rhs.x;
  }
};

foo现在已经定义了所有的排序和比较运算符。

int main() {
  foo one{1}, two{2};
  std::cout << (one < two) << "\n";
  std::cout << (one > two) << "\n";
  std::cout << (one == two) << "\n";
  std::cout << (one != two) << "\n";
  std::cout << (one <= two) << "\n";
  std::cout << (one >= two) << "\n";
  std::cout << (one == one) << "\n";
  std::cout << (one != one) << "\n";
  std::cout << (one <= one) << "\n";
  std::cout << (one >= one) << "\n";
}

Live example

所有这一切的要点是,作为一种语言,C ++并不认为<意味着>>===都有意义。但是您可以编写一个库,使您可以使用定义了<的类型,并且添加一个简单的基类会突然使所有其他操作定义为零运行时成本。

答案 8 :(得分:5)

答案是否定的,你只需要一个简单的测试

struct MyType{
    int value;
};

bool operator < (MyType& a, MyType& b)
{
    return a.value < b.value;
}

int main(int argc, char* argv[])
{
    MyType a = {3};
    MyType b = {4};
    if (a == b)
        std::cout << "a==b" << std::endl;
    if (a < b)
        std::cout << "a < b" << std::endl;
}

g ++ 4.8.2抱怨:

  

main.cpp:在函数'int main(int,char **)'中:

     

main.cpp:16:11:错误:'operator =='不匹配(操作数类型为'MyType'和'MyType')

但是在C ++中有类似的东西,请查看c++ concepts:Compare

它说:

  

equiv(a,b),一个等同于!comp(a,b)&amp;&amp;的表达式。 !comp(b,a)

答案 9 :(得分:4)

除了其他答案,

编译器甚至无法推断!=

中的==
struct MyType
{
    int value;
};

bool operator == (const MyType& a, const MyType& b)
{
    return a.value == b.value;
}

int main()
{
    MyType a = {3};
    MyType b = {4};
    if (a != b)    // (* compilation Error *) 
        std::cout << "a does not equal b" << std::endl;
}

如果有一个选项告诉编译器其余的理性运算符适用于您的类,那将是很好的。

正如<utility>标题中的一些答案所解释的那样可以提供这样的功能。您需要在main

的开头添加以下行
using namespace std::rel_ops; 

然而,如JDługosz所述,使用这种方法代价高昂并且会导致整个地方出现过载模糊。

答案 10 :(得分:3)

考虑以下示例:

class point{

  unsigned int x;
  unsigned int y;

  public:
    bool operator <(const point& other){
      return (x+y) < (other.x+other.y);
    }

    bool operator == (const point& other){
      return (x==other.x) && (y==other.y);
    }
}

然后我们有:

point a{1, 2};
point b{2, 1};

!(a&lt; b),!(b&lt; a),但也!(a == b)。

答案 11 :(得分:2)

有点 但是你需要boost::operators

  

类类型的重载运算符通常以组的形式出现。如果你   可以写x + y,你可能也想写x + = y。如果   你可以写x&lt; y,你也想要x&gt; y,x> = y,并且x <= y。   此外,除非你的班级确实有令人惊讶的行为,否则   这些相关的运算符可以用其他运算符来定义(例如x> = y   &LT; =&GT; !(x boost / operators.hpp 模板有帮助   通过基于其他的命名空间作用域为您生成运算符   你在班上定义的运算符。

答案 12 :(得分:1)

答案很清楚。没有隐含的方式。 C ++类允许运算符重载。所以,你的想法在逻辑上,if !(a < b) and !(b < a)意味着a == b。是正确的。并且,您可以如下重载运算符。例如,Fraction类:

class Fraction {
    int num;
    int denom;
    . . .
    public:
    . . .
    bool operator < (const Fraction &other) {
        if ((this->num * other.denom) < (this->denom * other.num))
            return false;
        else
            return true;
       }
       bool operator == (const Fraction &other) (
           if (!(*this < other) && !(other < *this)) {
               return true;
           else
               return false;
       }
};