为什么 C++ 编译器不定义 operator== 和 operator!=?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/217911/
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-27 13:48:41  来源:igfitidea点击:

Why don't C++ compilers define operator== and operator!=?

c++operators

提问by Rob

I am a big fan of letting the compiler do as much work for you as possible. When writing a simple class the compiler can give you the following for 'free':

我非常喜欢让编译器为你做尽可能多的工作。在编写一个简单的类时,编译器可以“免费”为您提供以下内容:

  • A default (empty) constructor
  • A copy constructor
  • A destructor
  • An assignment operator (operator=)
  • 默认(空)构造函数
  • 复制构造函数
  • 析构函数
  • 赋值运算符 ( operator=)

But it cannot seem to give you any comparison operators - such as operator==or operator!=. For example:

但它似乎无法为您提供任何比较运算符 - 例如operator==or operator!=。例如:

class foo
{
public:
    std::string str_;
    int n_;
};

foo f1;        // Works
foo f2(f1);    // Works
foo f3;
f3 = f2;       // Works

if (f3 == f2)  // Fails
{ }

if (f3 != f2)  // Fails
{ }

Is there a good reason for this? Why would performing a member-by-member comparison be a problem? Obviously if the class allocates memory then you'd want to be careful, but for a simple class surely the compiler could do this for you?

这有充分的理由吗?为什么逐个成员进行比较会成为问题?显然,如果该类分配内存,那么您需要小心,但是对于一个简单的类,编译器肯定可以为您做到这一点吗?

采纳答案by Mark Ingram

The compiler wouldn't know whether you wanted a pointer comparison or a deep (internal) comparison.

编译器不知道您是想要指针比较还是深度(内部)比较。

It's safer to just not implement it and let the programmer do that themselves. Then they can make all the assumptions they like.

不实现它而让程序员自己做更安全。然后他们可以做出他们喜欢的所有假设。

回答by Michael Burr

The argument that if the compiler can provide a default copy constructor, it should be able to provide a similar default operator==()makes a certain amount of sense. I think that the reason for the decision not to provide a compiler-generated default for this operator can be guessed by what Stroustrup said about the default copy constructor in "The Design and Evolution of C++" (Section 11.4.1 - Control of Copying):

如果编译器可以提供默认的复制构造函数,那么它应该能够提供类似的默认值的论点operator==()在一定程度上是有意义的。我认为决定不为这个运算符提供编译器生成的默认值的原因可以通过 Stroustrup 在“C++ 的设计和演化”(第 11.4.1 节 - 复制控制)中关于默认复制构造函数的说法来猜测:

I personally consider it unfortunate that copy operations are defined by default and I prohibit copying of objects of many of my classes. However, C++ inherited its default assignment and copy constructors from C, and they are frequently used.

我个人认为很遗憾,默认情况下定义了复制操作,并且我禁止复制我的许多类的对象。但是,C++ 从 C 继承了它的默认赋值和复制构造函数,并且它们经常被使用。

So instead of "why doesn't C++ have a default operator==()?", the question should have been "why does C++ have a default assignment and copy constructor?", with the answer being those items were included reluctantly by Stroustrup for backwards compatibility with C (probably the cause of most of C++'s warts, but also probably the primary reason for C++'s popularity).

因此,不是“为什么 C++ 没有默认值operator==()?”,问题应该是“为什么 C++ 有默认赋值和复制构造函数?”,答案是 Stroustrup 不情愿地包含了这些项目,以便与 C 向后兼容(可能是大多数 C++ 问题的原因,但也可能是 C++ 流行的主要原因)。

For my own purposes, in my IDE the snippet I use for new classes contains declarations for a private assignment operator and copy constructor so that when I gen up a new class I get no default assignment and copy operations - I have to explicitly remove the declaration of those operations from the private:section if I want the compiler to be able to generate them for me.

出于我自己的目的,在我的 IDE 中,我用于新类的代码段包含私有赋值运算符和复制构造函数的声明,因此当我生成一个新类时,我不会获得默认赋值和复制操作 - 我必须明确删除声明private:如果我希望编译器能够为我生成它们,则来自该部分的那些操作。

回答by Anton Savin

Even in C++20, the compiler still won't implicitly generate operator==for you

即使在 C++20 中,编译器仍然不会operator==为你隐式生成

struct foo
{
    std::string str;
    int n;
};

assert(foo{"Anton", 1} == foo{"Anton", 1}); // ill-formed

But you will gain the ability to explicitlydefault ==since C++20:

但是您将获得自 C++20 以来显式默认的能力:==

struct foo
{
    std::string str;
    int n;

    // either member form
    bool operator==(foo const&) const = default;
    // ... or friend form
    friend bool operator==(foo const&, foo const&) = default;
};

Defaulting ==does member-wise ==(in the same way that the default copy constructor does member-wise copy construction). The new rules also provide the expected relationship between ==and !=. For instance, with the declaration above, I can write both:

默认==是按成员进行的==(与默认复制构造函数执行按成员复制构造的方式相同)。新规则还提供了==和之间的预期关系!=。例如,使用上面的声明,我可以同时写:

assert(foo{"Anton", 1} == foo{"Anton", 1}); // ok!
assert(foo{"Anton", 1} != foo{"Anton", 2}); // ok!

This specific feature (defaulting operator==and symmetry between ==and !=) comes from one proposalthat was part of the broader language feature that is operator<=>.

这种特殊的功能(默认operator==和之间的对称==!=)来自一个建议,这是更广泛的语言特点,那就是的一部分operator<=>

回答by alexk7

IMHO, there is no "good" reason. The reason there are so many people that agree with this design decision is because they did not learn to master the power of value-based semantics. People need to write a lot of custom copy constructor, comparison operators and destructors because they use raw pointers in their implementation.

恕我直言,没有“好”的理由。之所以有这么多人同意这个设计决定,是因为他们没有学会掌握基于值的语义的力量。人们需要编写大量自定义复制构造函数、比较运算符和析构函数,因为它们在实现中使用原始指针。

When using appropriate smart pointers (like std::shared_ptr), the default copy constructor is usually fine and the obvious implementation of the hypothetical default comparison operator would be as fine.

当使用适当的智能指针(如 std::shared_ptr)时,默认的复制构造函数通常很好,假设的默认比较运算符的明显实现也会很好。

回答by Rio Wing

It's answered C++ didn't do == because C didn't, and here is why C provides only default = but no == at first place. C wanted to keep it simple: C implemented = by memcpy; however, == cannot be implemented by memcmp due to padding. Because padding is not initialized, memcmp says they are different even though they are the same. The same problem exists for empty class: memcmp says they are different because size of empty classes are not zero. It can be seen from above that implementing == is more complicated than implementing = in C. Some code exampleregarding this. Your correction is appreciated if I'm wrong.

答案是 C++ 没有做 == 因为 C 没有,这就是为什么 C 最初只提供默认 = 而没有提供 ==。C 想保持简单:C 实现 = 由 memcpy; 但是,由于填充,memcmp 无法实现 ==。因为 padding 没有初始化,所以 memcmp 说它们是不同的,即使它们是相同的。空类存在同样的问题:memcmp 说它们不同,因为空类的大小不为零。从上面可以看出,实现 == 比在 C 中实现 = 更复杂。一些关于此的代码示例。如果我错了,感谢您的更正。

回答by Nikos Athanasiou

In this videoAlex Stepanov, the creator of STL addresses this very question at about 13:00. To summarize, having watched the evolution of C++ he argues that:

在这段视频中,STL 的创建者 Alex Stepanov 在大约 13:00 解决了这个问题。总而言之,在观察了 C++ 的演变之后,他认为:

  • It's unfortunate that == and !=are not implicitly declared (and Bjarne agrees with him). A correct language should have those things ready for you (he goes further on to suggest you should not be able to define a !=that breaks the semantics of ==)
  • The reason this is the case has its roots (as many of C++ problems) in C. There, the assignment operator is implicitly defined with bit by bit assignmentbut that wouldn't work for ==. A more detailed explanation can be found in this articlefrom Bjarne Stroustrup.
  • In the follow up question Why then wasn't a member by member comparison usedhe says an amazing thing: C was kind of a homegrown language and the guy implementing these stuff for Ritchie told him he found this to be hard to implement!
  • 不幸的是== 和 !=没有隐式声明(Bjarne 同意他的观点)。正确的语言应该为你准备好这些东西(他进一步建议你不应该定义一个破坏==语义的!=
  • 出现这种情况的原因在 C 中有其根源(与许多 C++ 问题一样)。在那里,赋值运算符是用逐位赋值隐式定义的,但这不适用于==。可以在Bjarne Stroustrup 的这篇文章中找到更详细的解释。
  • 在后续问题中,为什么不使用成员比较,他说了一件了不起的事情:C 是一种本土语言,为 Ritchie 实现这些东西的人告诉他,他发现这很难实现!

He then says that in the (distant) future ==and !=will be implicitly generated.

然后他说在(遥远的)未来==!=将被隐式生成。

回答by vll

C++20 provides a way to easily implement a default comparison operator.

C++20 提供了一种轻松实现默认比较运算符的方法。

Example from cppreference.com:

来自cppreference.com 的示例:

class Point {
    int x;
    int y;
public:
    auto operator<=>(const Point&) const = default;
    // ... non-comparison functions ...
};

// compiler implicitly declares operator== and all four relational operators work
Point pt1, pt2;
if (pt1 == pt2) { /*...*/ } // ok, calls implicit Point::operator==
std::set<Point> s; // ok
s.insert(pt1); // ok
if (pt1 <= pt2) { /*...*/ } // ok, makes only a single call to Point::operator<=>

回答by sergtk

It is not possible to define default ==, but you can define default !=via ==which you usually should define yourselves. For this you should do following things:

无法定义 default ==,但您可以定义 default ,您通常应该!=通过==它来定义自己。为此,您应该做以下事情:

#include <utility>
using namespace std::rel_ops;
...

class FooClass
{
public:
  bool operator== (const FooClass& other) const {
  // ...
  }
};

You can see http://www.cplusplus.com/reference/std/utility/rel_ops/for details.

您可以查看http://www.cplusplus.com/reference/std/utility/rel_ops/了解详情。

In addition if you define operator< , operators for <=, >, >= can be deduced from it when using std::rel_ops.

另外如果你定义了operator< ,<=、>、>=的操作符可以在使用时从中推导出来std::rel_ops

But you should be careful when you use std::rel_opsbecause comparison operators can be deduced for the types you are not expected for.

但是您在使用时应该小心,std::rel_ops因为可以为您不期望的类型推导出比较运算符。

More preferred way to deduce related operator from basic one is to use boost::operators.

从基本运算符推导出相关运算符的更优选方法是使用boost::operators

The approach used in boost is better because it define the usage of operator for the class you only want, not for all classes in scope.

boost 中使用的方法更好,因为它为您只需要的类定义了运算符的用法,而不是为范围内的所有类。

You can also generate "+" from "+=", - from "-=", etc... (see full list here)

您还可以从“+=”生成“+”,从“-=”生成-,等等...(请参阅此处的完整列表)

回答by MSalters

C++0x hashad a proposal for default functions, so you could say default operator==;We've learnt that it helps to make these things explicit.

C++0x一个关于默认函数的提议,所以你可以说default operator==;我们已经了解到它有助于使这些事情变得明确。

回答by Paul de Vrieze

Conceptually it is not easy to define equality. Even for POD data, one could argue that even if the fields are the same, but it is a different object (at a different address) it is not necessarily equal. This actually depends on the usage of the operator. Unfortunately your compiler is not psychic and cannot infer that.

从概念上讲,定义平等并不容易。即使对于 POD 数据,人们也可能会争辩说,即使字段相同,但它是不同的对象(在不同的地址),它也不一定相等。这实际上取决于运营商的使用情况。不幸的是,您的编译器不是通灵的,无法推断出这一点。

Besides this, default functions are excellent ways to shoot oneself in the foot. The defaults you describe are basically there to keep compatibility with POD structs. They do however cause more than enough havoc with developers forgetting about them, or the semantics of the default implementations.

除此之外,默认功能是用脚射击自己的绝佳方式。您描述的默认值基本上是为了保持与 POD 结构的兼容性。然而,它们确实造成了足够的破坏,开发人员忘记了它们,或者默认实现的语义。