Bar*_*arc 292 c++ equality operator-overloading equality-operator
我学习C++操作符重载,我看到==和!=仅仅是可定制的用户定义类型的一些特殊功能.但我担心的是,为什么需要两个单独的定义?我认为如果a == b是真的,则a != b自动为假,反之亦然,并且没有其他可能性,因为根据定义,a != b是!(a == b).我无法想象任何情况都不是这样.但也许我的想象力是有限的,或者我对某些东西一无所知?
我知道我可以用另一个来定义一个,但这不是我要问的.我也没有询问按价值或身份比较对象之间的区别.或者两个对象是否可以同时相等且不相等(这绝对不是一个选项!这些东西是相互排斥的).我问的是这个:
是否有任何情况可以提出有关两个对象相等的问题是否有意义,但是询问它们不相等是没有意义的?(无论是从用户的角度,还是从实施者的角度来看)
如果没有这种可能性,那么为什么地球上C++会将这两个运算符定义为两个不同的函数?
小智 272
您不希望语言自动重写a != b,!(a == b)因为a == b返回的内容不是a bool.你可以做到这一点有几个原因.
您可能有表达式构建器对象,其中a == b没有并且不打算执行任何比较,而只是构建一些表达式节点表示a == b.
您可能有懒惰的评估,其中a == b没有并且不打算直接执行任何比较,而是返回某种lazy<bool>可以bool在稍后时间隐式或显式转换为实际执行比较的类型.可能与表达式构建器对象结合使用,以便在评估之前完成表达式优化.
您可能有一些自定义optional<T>模板类,其中给定可选变量,t并且u您希望允许t == u,但让它返回optional<bool>.
可能还有更多我没想到的.即使在这些例子中,操作a == b和a != b两者都有意义,但仍然a != b不是一样的!(a == b),所以需要单独的定义.
shr*_*ike 110
如果没有这种可能性,那么为什么地球上C++会将这两个运算符定义为两个不同的函数?
因为你可以重载它们,并且通过重载它们,你可以给它们与原始它们完全不同的含义.
例如,运算符<<,最初是按位左移运算符,现在通常作为插入运算符重载,如std::cout << something; 与原来完全不同的意思.
因此,如果您接受操作符的含义在重载时发生更改,那么就没有理由阻止用户向操作符赋予意义,==这不完全是操作符的否定!=,尽管这可能会令人困惑.
Tre*_*key 60
但我担心的是,为什么需要两个单独的定义?
您不必同时定义它们.
如果它们是互斥的,那么您仍然可以通过仅定义==和<旁边的std :: rel_ops来简明扼要
Fom cppreference:
#include <iostream>
#include <utility>
struct Foo {
int n;
};
bool operator==(const Foo& lhs, const Foo& rhs)
{
return lhs.n == rhs.n;
}
bool operator<(const Foo& lhs, const Foo& rhs)
{
return lhs.n < rhs.n;
}
int main()
{
Foo f1 = {1};
Foo f2 = {2};
using namespace std::rel_ops;
//all work as you would expect
std::cout << "not equal: : " << (f1 != f2) << '\n';
std::cout << "greater: : " << (f1 > f2) << '\n';
std::cout << "less equal: : " << (f1 <= f2) << '\n';
std::cout << "greater equal: : " << (f1 >= f2) << '\n';
}
Run Code Online (Sandbox Code Playgroud)
是否有任何情况可以提出有关两个对象相等的问题是否有意义,但是询问它们不相等是没有意义的?
我们经常将这些运算符与平等相关联
虽然这就是它们在基本类型上的行为方式,但没有义务这是它们在自定义数据类型上的行为.如果你不想,你甚至不需要返回布尔.
我看到人们以奇怪的方式超载运营商,却发现它对于他们的域特定应用是有意义的.即使界面看起来表明它们是互斥的,作者也可能想要添加特定的内部逻辑.
(无论是从用户的角度,还是从实施者的角度来看)
我知道你想要一个具体的例子,
所以这里有一个我认为实用的Catch测试框架:
template<typename RhsT>
ResultBuilder& operator == ( RhsT const& rhs ) {
return captureExpression<Internal::IsEqualTo>( rhs );
}
template<typename RhsT>
ResultBuilder& operator != ( RhsT const& rhs ) {
return captureExpression<Internal::IsNotEqualTo>( rhs );
}
Run Code Online (Sandbox Code Playgroud)
这些运算符正在做不同的事情,将一个方法定义为另一个方法(而不是另一个方法)是没有意义的.这样做的原因是框架可以打印出比较结果.为此,它需要捕获使用了重载运算符的上下文.
Jan*_*der 42
有些情况下,一些非常完善的约定(a == b)和(a != b)是两个假不一定是对立的.特别是,在SQL中,与NULL的任何比较都会产生NULL,而不是true或false.
如果可能的话,创建新的示例可能不是一个好主意,因为它太不直观了,但是如果你想模拟一个现有的约定,那么选择让你的操作符为"正确"行为是很好的.上下文.
Cen*_*ril 23
我只回答你问题的第二部分,即:
如果没有这种可能性,那么为什么地球上C++会将这两个运算符定义为两个不同的函数?
允许开发人员超载的一个原因是性能.您可以通过实现==和来实现优化!=.然后x != y可能比便宜!(x == y).有些编译器可能能够为您优化它,但也许不是,特别是如果您有涉及大量分支的复杂对象.
即使在Haskell中,开发者拿法律和数学概念非常重视,一个仍然允许超载既==和/=,因为你可以在这里看到(http://hackage.haskell.org/package/base-4.9.0.0/docs/Prelude .html #v:-61--61-):
$ ghci
GHCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help
?> :i Eq
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
-- Defined in `GHC.Classes'
Run Code Online (Sandbox Code Playgroud)
这可能被认为是微优化,但在某些情况下可能是有保证的.
Ben*_*ley 16
是否有任何情况可以提出有关两个对象相等的问题是否有意义,但是询问它们不相等是没有意义的?(无论是从用户的角度,还是从实施者的角度来看)
这是一个意见.也许它没有.但语言设计师并非无所不知,他们决定不限制那些可能会提出可能有意义的情况的人(至少对他们而言).
Nia*_*all 13
回应编辑;
也就是说,如果有些类型可能有操作员
==而不是操作员!=,反之亦然,并且何时有意义.
在一般的,不,这是没有意义的.平等和关系运营商通常都是成套的.如果有平等,那么不平等也是如此; 小于,则大于等与<=等类似的方法被施加到算术运算符,以及,它们也通常有自然的逻辑集.
这在std::rel_ops命名空间中得到证明.如果您实现了等于和小于运算符,那么使用该命名空间可以为您提供其他服务,根据您原来实现的运算符实现.
所有人都说,是否有条件或情况,一方不会立即指另一方,或无法以其他方式实施?是的,可能很少,但他们在那里; 再次,正如rel_ops它自己的名称空间所证明的那样.因此,允许它们独立实现允许您利用该语言以对代码的用户或客户端仍然自然且直观的方式获取您需要或需要的语义.
已经提到的懒惰评估就是一个很好的例子.另一个很好的例子是赋予它们语义,这些语义并不意味着平等或不平等.与此类似的示例是位移操作符<<并>>用于流插入和提取.虽然在一般的圈子中可能不赞成,但在一些领域特定的领域,它可能是有意义的.
Tay*_*wee 12
如果==和!=运算符实际上并不意味着相等,那么<<和>>流运算符并不意味着位移.如果将符号视为某些其他概念,则它们不必相互排斥.
在平等方面,如果您的用例保证将对象视为不可比较,则可能有意义,因此每次比较都应返回false(如果您的运算符返回非bool,则返回非可比较的结果类型).我不能想到这是有必要的具体情况,但我可以看到它足够合理.
enum BoolPlus {
kFalse = 0,
kTrue = 1,
kFileNotFound = -1
}
BoolPlus operator==(File& other);
BoolPlus operator!=(File& other);
Run Code Online (Sandbox Code Playgroud)
我不能证明这个运算符重载是正确的,但在上面的例子中,不可能将其定义operator!=为"反向" operator==.