如果我只实现了运算符<,我可以使用运算符 == 吗?

Could I use operator == if I only implemented operator <?

本文关键字:运算符 可以使 我可以 lt 实现 如果      更新时间:2023-10-16

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

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

C++无法自动推断这一点,原因如下:

  1. 将每个类型都与operator<进行比较是没有意义的,因此该类型不一定定义operator<
    • 这意味着operator==不能根据operator<自动定义
  2. operator<不需要比较其参数。程序员可以为他们的类型定义运算符,以对他们的参数执行几乎任何操作
    • 这意味着你关于!(a < b) && !(b < a)等同于a == b的陈述可能不一定是正确的,假设这些运算符是定义的。

如果你想要一个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;
}

它无法从<推断==,因为并非所有类型都是有序的,例如std::complex2 + 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,除了最后一个

No.此方法适用于称为完全有序的类数字对象。对于各种集合/类,没有人能保证这种关系。甚至没有人能保证operator <会比较一些东西。

所以==无非是==.你可以通过<实现==,但这并不适合所有人,C++标准也不会为你做。

从逻辑上讲,如果!(a <b)><a)>

来表达其他人用数学术语陈述的内容:假设您有一个返回bool并定义严格弱订单的operator <,并且您将operator ==实现为返回!(a < b) && !(b < a),那么这个运算符定义了与给定的严格顺序一致的等价关系。然而,C++既不需要operator <来定义严格的弱序,也不需要operator ==定义等价关系(尽管许多标准算法(如sort)可能隐式使用这些运算符,并要求严格的弱阶 rsp. 等价关系)。

如果你想根据operator <的严格弱顺序定义所有其他关系运算符,Boost.Operators 可能会为你节省一些类型。

因为很容易滥用不符合标准算法要求的operator <,例如通过std::sortstd::lower_bound等意外使用它,我建议operator <定义为严格的弱顺序或根本不使用。CodesInChaos给出的例子是一个偏序,它不符合严格弱序的"不可比性的传递性"要求。因此,我建议用不同的名称来称呼这种关系,例如bool setLess(const MySet &, const MySet &).

来源:

  • https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings
  • http://en.cppreference.com/w/cpp/concept/Compare

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)在数学上是等价的。

编译器不会从<推断==

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

#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 << "equalsn";
}
return 0;
}

GCC会给出此错误:

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

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

这并不意味着从<==和整个无数不应该很容易

boost::运算符试图使它变得简单。 使用它并完成。

如果你想自己做,也只需要一点代码来重新实现boost为你提供的东西:

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提升中获得等效的cose。

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

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";
}

活生生的例子。

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

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

它不会根据需要基于较少运算符定义相等运算符。

这仍然非常有用;如果您定义较少运算符和相等运算符,您将免费获得其他比较运算符。

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

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:错误:与"运算符=="不匹配(操作数类型为"MyType"和"MyType")

但是有类似的东西在C++中起作用,检查这个c ++概念:比较

它说:

equiv(a, b),一个等价于 !comp(a, b) && !comp(b, a) 的表达式

除了其他答案,

编译器甚至无法从==推断出!=

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所指出的那样,使用这种方法的成本很高,并且会导致到处都是过载歧义。

考虑以下示例:

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};

!(一<乙),!(b>

有点。
但是你需要提升::运算符

类类型的重载运算符通常发生在组中。如果你 可以写 x + y,你可能也希望能够写 x += y。如果 你可以写 x <y,你还需要> y,x>= y 和 x <= y。 此外,除非你的班级真的有令人惊讶的行为,否则一些 这些相关运算符可以用其他运算符来定义(例如 x>= y <=> !(x <y))。为多个类复制此样板是>boost/operator.hpp 模板有助于 通过在命名空间范围内基于其他 您在类中定义的运算符。

答案显然是否定的。没有隐含的方法。C++类允许运算符过载。所以,你的想法从逻辑上讲,if !(a < b) and !(b < a)这意味着a == b.是正确的。并且,您可以按如下方式重载运算符。例如,分数类:

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;
}
};