C++ == 和 != 是相互依赖的吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37800001/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 14:43:05  来源:igfitidea点击:

Are == and != mutually dependent?

c++operator-overloadingequalityequality-operator

提问by BarbaraKwarc

I'm learning about operator overloading in C++, and I see that ==and !=are simply some special functions which can be customized for user-defined types. My concern is, though, why are there two separatedefinitions needed? I thought that if a == bis true, then a != bis automatically false, and vice versa, and there is no other possibility, because, by definition, a != bis !(a == b). And I couldn't imagine any situation in which this wasn't true. But perhaps my imagination is limited or I am ignorant of something?

我正在学习 C++ 中的运算符重载,我看到了这一点==!=并且只是一些可以为用户定义类型自定义的特殊函数。不过,我担心的是,为什么需要两个单独的定义?我认为如果a == b为真,则a != b自动为假,反之亦然,没有其他可能性,因为根据定义,a != b!(a == b)。我无法想象这不是真的。但也许我的想象力有限,或者我对某些事情一无所知?

I know that I can define one in terms of the other, but this is not what I'm asking about. I'm also not asking about the distinction between comparing objects by value or by identity. Or whether two objects could be equal and non-equal at the same time (this is definitely not an option! these things are mutually exclusive). What I'm asking about is this:

我知道我可以根据另一个来定义一个,但这不是我要问的。我也不是在问按值或按身份比较对象之间的区别。或者两个对象是否可以同时相等和不相等(这绝对不是一个选项!这些东西是相互排斥的)。我要问的是这个:

Is there any situation possible in which asking questions about two objects being equal does make sense, but asking about them notbeing equal doesn't make sense? (either from the user's perspective, or the implementer's perspective)

是否有任何情况下,询问两个对象相等的问题确实有意义,但询问它们相等的问题没有意义?(无论是从用户的角度,还是从实现者的角度)

If there is no such possibility, then why on Earth does C++ have these two operators being defined as two distinct functions?

如果没有这种可能性,那么为什么 C++ 将这两个运算符定义为两个不同的函数?

采纳答案by BarbaraKwarc

You would notwant the language to automatically rewrite a != bas !(a == b)when a == breturns something other than a bool. And there are a few reasons why you might make it do that.

你会不会想使用的语言自动改写a != b!(a == b)a == b比其他收益东西bool。您可能会这样做有几个原因。

You may have expression builder objects, where a == bdoesn't and isn't intended to perform any comparison, but simply builds some expression node representing a == b.

您可能有表达式构建器对象,其中a == b没有也不打算执行任何比较,而只是构建一些表示a == b.

You may have lazy evaluation, where a == bdoesn't and isn't intended to perform any comparison directly, but instead returns some kind of lazy<bool>that can be converted to boolimplicitly or explicitly at some later time to actually perform the comparison. Possibly combined with the expression builder objects to allow complete expression optimisation before evaluation.

您可能有惰性求值,其中a == b不打算也不打算直接执行任何比较,而是返回某种lazy<bool>可以bool在以后隐式或显式转换为实际执行比较的类型。可能与表达式构建器对象相结合,以允许在评估之前进行完整的表达式优化。

You may have some custom optional<T>template class, where given optional variables tand u, you want to allow t == u, but make it return optional<bool>.

您可能有一些自定义optional<T>模板类,其中给定了可选变量tu,您希望允许t == u,但使其返回optional<bool>

There's probably more that I didn't think of. And even though in these examples the operation a == band a != bdo both make sense, still a != bisn't the same thing as !(a == b), so separate definitions are needed.

可能还有更多我没有想到的。即使在这些示例中,操作a == ba != bdo 都有意义,但仍然a != b与 不同!(a == b),因此需要单独的定义。

回答by shrike

If there is no such possibility, then why on Earth does C++ have these two operators being defined as two distinct functions?

如果没有这种可能性,那么为什么 C++ 将这两个运算符定义为两个不同的函数?

Because you can overload them, and by overloading them you can give them a totally different meaning from their original one.

因为您可以重载它们,并且通过重载它们,您可以赋予它们与原始含义完全不同的含义。

Take, for example, operator <<, originally the bitwise left shift operator, now commonly overloaded as an insertion operator, like in std::cout << something; totally different meaning from the original one.

例如, operator <<,最初是按位左移运算符,现在通常作为插入运算符重载,如 in std::cout << something; 和原来的意思完全不同。

So, if you accept that the meaning of an operator changes when you overload it, then there is no reason to prevent user from giving a meaning to operator ==that is not exactly the negationof operator !=, though this might be confusing.

因此,如果您接受运算符重载时其含义会发生变化,那么没有理由阻止用户赋予运算符的含义==不完全是运算符的否定!=,尽管这可能会令人困惑。

回答by Trevor Hickey

My concern is, though, why are there two separate definitions needed?

不过,我担心的是,为什么需要两个单独的定义?

You don't have to define both.
If they are mutually exclusive, you can still be concise by only defining ==and <alongside std::rel_ops

您不必定义两者。
如果它们是互斥的,您仍然可以通过仅定义std::rel_ops==<std::rel_ops 一起来简洁

Fom cppreference:

格式 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';
}

Is there any situation possible in which asking questions about two objects being equal does make sense, but asking about them not being equal doesn't make sense?

是否有任何情况下,询问两个对象相等的问题确实有意义,但询问它们不相等的问题没有意义?

We often associate these operators to equality.
Although that is how they behave on fundamental types, there is no obligation that this be their behaviour on custom data types. You don't even have to return a bool if you don't want to.

我们经常将这些运算符与等式联系起来。
尽管这是它们在基本类型上的行为方式,但没有义务将它们作为自定义数据类型的行为方式。如果您不想,您甚至不必返回 bool。

I've seen people overload operators in bizarre ways, only to find that it makes sense for their domain specific application. Even if the interface appears to show that they are mutually exclusive, the author may want to add specific internal logic.

我见过人们以奇怪的方式重载运算符,却发现这对他们的领域特定应用程序有意义。即使界面看起来显示它们是互斥的,作者可能还想添加特定的内部逻辑。

(either from the user's perspective, or the implementer's perspective)

(无论是从用户的角度,还是从实现者的角度)

I know you want a specific example,
so here is one from the Catch testing frameworkthat I thought was practical:

我知道你想要一个具体的例子,
所以这里有一个来自我认为实用的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 );
}

These operators are doing different things, and it would not make sense to define one method as a !(not) of the other. The reason this is done, is so that the framework can print out the comparison made. In order to do that, it needs to capture the context of what overloaded operator was used.

这些运算符在做不同的事情,将一种方法定义为另一种方法的 !(not) 是没有意义的。这样做的原因是,框架可以打印出所做的比较。为了做到这一点,它需要捕获使用重载运算符的上下文。

回答by Jander

There are some very well-established conventions in which (a == b)and (a != b)are both falsenot necessarily opposites. In particular, in SQL, any comparison to NULL yields NULL, not true or false.

有一些非常完善的约定,其中(a == b)(a != b)都是错误的,不一定是对立的。特别是,在 SQL 中,任何与 NULL 的比较都会产生 NULL,而不是 true 或 false。

It's probably not a good idea to create new examples of this if at all possible, because it's so unintuitive, but if you're trying to model an existing convention, it's nice to have the option to make your operators behave "correctly" for that context.

如果可能的话,为此创建新示例可能不是一个好主意,因为它太不直观了,但是如果您尝试对现有约定进行建模,那么可以选择让您的操作员为此“正确”表现语境。

回答by Centril

I will only answer the second part of your question, namely:

我只回答你问题的第二部分,即:

If there is no such possibility, then why on Earth does C++ have these two operators being defined as two distinct functions?

如果没有这种可能性,那么为什么 C++ 将这两个运算符定义为两个不同的函数?

One reason why it makes sense to allow the developer to overload both is performance. You might allow optimizations by implementing both ==and !=. Then x != ymight be cheaper than !(x == y)is. Some compilers may be able to optimize it for you, but perhaps not, especially if you have complex objects with a lot of branching involved.

允许开发人员重载两者是有意义的一个原因是性能。您可以通过同时实现==和来允许优化!=。然后x != y可能比!(x == y)现在便宜。一些编译器可能能够为您优化它,但也许不能,特别是如果您有涉及大量分支的复杂对象。

Even in Haskell, where developers take laws and mathematical concepts very seriously, one is still allowed to overload both ==and /=, as you can see here (http://hackage.haskell.org/package/base-4.9.0.0/docs/Prelude.html#v:-61--61-):

即使在开发人员非常重视法律和数学概念的 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'

This would probably be considered micro-optimization, but it might be warranted for some cases.

这可能被视为微优化,但在某些情况下可能是必要的。

回答by Benjamin Lindley

Is there any situation possible in which asking questions about two objects being equal does make sense, but asking about them not being equal doesn't make sense? (either from the user's perspective, or the implementer's perspective)

是否有任何情况下,询问两个对象相等的问题确实有意义,但询问它们不相等的问题没有意义?(无论是从用户的角度,还是从实现者的角度)

That's an opinion. Maybe it doesn't. But the language designers, not being omniscient, decided not to restrict people who might come up with situations in which it might make sense (at least to them).

那是一种意见。也许不是。但是语言设计者并非无所不知,决定不限制那些可能想出可能有意义的情况的人(至少对他们而言)。

回答by Niall

In response to the edit;

响应编辑;

That is, if it is possible for some type to have the operator ==but not the !=, or vice versa, and when does it make sense to do so.

也就是说,如果某种类型可能有运算符==而不是!=,反之亦然,以及何时这样做有意义。

In general, no, it doesn't make sense. Equality and relational operators generally come in sets. If there is the equality, then the inequality as well; less than, then greater than and so on with the <=etc. A similar approach is applied to the arithmetic operators as well, they also generally come in natural logical sets.

一般的,不,这是没有意义的。等式和关系运算符通常成套出现。有等式,则不等式也有;小于,然后大于<=等等等等。类似的方法也适用于算术运算符,它们通常也来自自然逻辑集合。

This is evidenced in the std::rel_opsnamespace. If you implement the equality and less than operators, using that namespace gives you the others, implemented in terms of your original implemented operators.

这在std::rel_ops命名空间中得到了证明。如果您实现等于和小于运算符,则使用该命名空间将为您提供其他运算符,根据您最初实现的运算符实现。

That all said, are there conditions or situations where the one would not immediately mean the other, or could not be implemented in terms of the others? Yes there are, arguably few, but they are there; again, as evidenced in the rel_opsbeing a namespace of its own. For that reason, allowing them to be implemented independently allows you to leverage the language to get the semantics you require or need in a way that is still natural and intuitive for the user or client of the code.

了这么多是否有条件或情况,一个不会立即意味着另一个,或者不能以其他方式实施?是的,可以说很少,但它们在那里;再次,正如rel_ops它自己的命名空间所证明的那样。出于这个原因,允许它们独立实现允许您利用语言以对代码的用户或客户端仍然自然和直观的方式获得您需要或需要的语义。

The lazy evaluation already mentioned is an excellent example of this. Another good example is giving them semantics that don't mean equality or in-equality at all. A similar example to this is the bit shift operators <<and >>being used for stream insertion and extraction. Although it may be frowned upon in general circles, in some domain specific areas it may make sense.

已经提到的惰性求值就是一个很好的例子。另一个很好的例子是为它们提供根本不意味着平等或不平等的语义。与此类似的示例是位移运算符<<>>用于流插入和提取。尽管它在一般圈子中可能不受欢迎,但在某些特定领域中它可能是有意义的。

回答by Taywee

If the ==and !=operators don't actually imply equality, in the same way that the <<and >>stream operators don't imply bit-shifting. If you treat the symbols as if they mean some other concept, they don't have to be mutually exclusive.

如果==and!=运算符实际上并不暗示相等,就像<<and>>流运算符不暗示位移一样。如果您将符号视为表示其他概念,则它们不必相互排斥。

In terms of equality, it could make sense if your use-case warrants treating objects as non-comparable, so that every comparison should return false (or a non-comparable result type, if your operators return non-bool). I can't think of a specific situation where this would be warranted, but I could see it being reasonable enough.

就相等而言,如果您的用例保证将对象视为不可比较的,那么每次比较都应返回 false(或不可比较的结果类型,如果您的运算符返回非布尔值),这可能是有意义的。我想不出有必要这样做的具体情况,但我认为这足够合理。

回答by It'sPete

With great power comes great responsibly, or at least really good style guides.

强大的力量伴随着巨大的责任,或者至少是非常好的风格指南。

==and !=can be overloaded to do whatever the heck you want. It's both a blessing and a curse. There's no guarantee that !=means !(a==b).

==并且!=可以超载做任何你想做的事。这既是福也是祸。不能保证这!=意味着!(a==b)

回答by Dafang Cao

enum BoolPlus {
    kFalse = 0,
    kTrue = 1,
    kFileNotFound = -1
}

BoolPlus operator==(File& other);
BoolPlus operator!=(File& other);

I can't justify this operator overloading, but in the example above it is impossible to define operator!=as the "opposite" of operator==.

我不能证明这个运算符重载是合理的,但在上面的例子中,不可能定义operator!=operator==.