如果我只实现了operator <?,我可以使用operator ==吗?
我为某个对象实现了operator<
。 逻辑上,如果!(a < b)
和!(b < a)
表示a == b
。
这是自动推断? 我可以使用==
如果我只实现<
?
由于几个原因,C ++不能自动推断出这一点:
- 每一种types都与
operator<
进行比较是没有意义的,所以types可能不一定定义operator<
。- 这意味着
operator==
不能根据operator<
自动定义
- 这意味着
-
operator<
不需要比较它的参数。 程序员可以为其types定义运算符,以对其参数做任何事情 。- 这意味着你的关于
!(a < b) && !(b < a)
等价于a == b
的语句可能不一定是真的,假设这些操作符是被定义的。
- 这意味着你的关于
如果你想为你的types使用一个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; }
它不能推断==
,因为不是所有的types都被sorting,比如std::complex
。 是2 + 3i > 1 + 4i
还是不是?
而且,即使在正常sorting的types中,仍然无法从>
或<
,例如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';
除了最后一个,他们都会回来
不可以。这种方法适用于被称为完全sorting的类数对象。 对于各种各样的组/类,没有人能保证这种关系。 即使没有人能够保证operator <
将比较的东西。
所以==
没有什么别的==
。 你可以通过实现==
,但这不适用于每个人,C ++标准不会为你做。
逻辑上,如果!(a <b)和!(b <a)表示a == b。 c ++会自动推断这个吗? 如果我只实施,我可以使用==
把其他人用math术语说的:假设你有一个operator <
返回bool
并定义了一个严格的弱顺序 ,并且你实现operator ==
返回!(a < b) && !(b < a)
,那么这个算子定义了一个与给定的严格弱顺序一致的等价关系 。 然而,C ++既不需要operator <
来定义一个严格的弱顺序,也不需要operator ==
来定义一个等价关系(虽然很多标准algorithm如sort
可能会隐含地使用这些运算符,并且需要一个严格的弱顺序等价关系)。
如果你想定义所有其他的关系运算符,并且与你的operator <
的严格弱顺序一致,Boost.Operators可以为你节省一些input。
因为滥用不符合标准algorithm要求的operator <
(例如,通过std::sort
, std::lower_bound
等意外使用它)很容易,所以我推荐将operator <
或者定义为严格的弱顺序在所有。 CodesInChaos给出的例子是一个偏序,不符合严格弱顺序的“不可比性的传递性”要求。 因此,我build议用不同的名称来调用这样的关系,比如bool setLess(const MySet &, const MySet &)
。
资料来源:
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)
在math上是等价的。
编译器不会从<
推断==
。
你可以用一个简单的例子来检查:
#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) {
正如许多人所说,不,你不能,也不是编译器不应该。
这并不意味着从一个<
==
和整个无数的事物都不容易 。
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
boost中获得等价的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
已经定义了所有的sorting和比较运算符。
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 ++并不像一种语言那样假设<
means >
和>=
和==
都是有意义的。 但是你可以编写一个库,让你用<
defined定义一个types,然后添加一个微不足道的基类,让所有这些其他操作被定义为零运行时代价。
在std::rel_ops
命名空间中定义了一些模板,这些模板是自动定义缺less的操作符。
它没有定义一个基于较less运算符的相等运算符,如你所愿。
这仍然是非常有用的。 如果您定义了较less的运算符和相等运算符,则您将免费使用其他比较运算符。
答案是否定的,你只需要一个简单的testing
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 =='(操作数types是'MyType'和'MyType')
但有一些类似的工作在C ++中,检查这个c + +概念:比较
它说:
equiv(a,b),相当于!comp(a,b)&&!comp(b,a)
除了其他答案之外,
编译器甚至不能推断!=
from ==
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>
标题的一些答案中所解释的那样,可以提供这样的function。 您将需要在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};
!(a <b),!(b <a),还有!(a == b)。
有点。
但是你需要boost ::运算符
class级types的重载操作符通常以组的forms出现。 如果你可以写x + y,你可能也想写x + = y。 如果你可以写x <y,你也希望x> y,x> = y和x <= y。 而且,除非你的类具有真正令人惊讶的行为,否则这些相关的操作符中的一些可以用其他方式来定义(例如x> = y <=>!(x <y))。 为多个类复制这个样板既枯燥又容易出错。 boost / operators.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; } };