我非常支持让编译器为你做尽可能多的工作。当编写一个简单的类时,编译器可以为你提供以下“免费”:

默认(空)构造函数 复制构造函数 一个析构函数 赋值运算符(operator=)

但是它似乎不能给你任何比较操作符——比如operator==或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
{ }

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


当前回答

我同意,对于POD类型的类,编译器可以为你做。然而,你可能认为简单的编译器可能会出错。所以最好还是让程序员来做。

我曾经有一个POD案例,其中两个字段是唯一的-所以比较永远不会被认为是正确的。然而,我所需要的比较只是在有效负载上进行比较——这是编译器永远无法理解或自己无法解决的问题。

此外,他们不需要花很长时间来写,不是吗?!

其他回答

定义default ==是不可能的,但是你可以定义default != via ==,你通常应该定义自己。 为此,你应该做以下事情:

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

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

详情请访问http://www.cplusplus.com/reference/std/utility/rel_ops/。

此外,如果您定义操作符<,则在使用std::rel_ops时,可以从它推导出<=、>、>=的操作符。

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

从基本操作符推断相关操作符的更可取的方法是使用boost::操作符。

boost中使用的方法更好,因为它为您只需要的类定义了operator的用法,而不是为作用域内的所有类定义了operator的用法。

您还可以从“+=”生成“+”,从“-=”生成“-”,等等…(见完整列表)

这样,随着时间的推移,这个问题的答案仍然是完整的:从c++ 20开始,它可以通过命令auto操作符自动生成<=>(const foo&) const = default;

它将生成所有的运算符:==,!=,<,<=,>和>=,详细信息请参见https://en.cppreference.com/w/cpp/language/default_comparisons。

由于操作员的外观<=>,它被称为宇宙飞船操作员。另见为什么在c++中我们需要太空船<=>操作符?

EDIT:同样在c++ 11中,std::tie也提供了一个相当简洁的替代品,有关bool操作符<(…)的完整代码示例,请参阅https://en.cppreference.com/w/cpp/utility/tuple/tie。有趣的部分,改变为使用==是:

#include <tuple>

struct S {
………
bool operator==(const S& rhs) const
    {
        // compares n to rhs.n,
        // then s to rhs.s,
        // then d to rhs.d
        return std::tie(n, s, d) == std::tie(rhs.n, rhs.s, rhs.d);
    }
};

tie适用于所有比较操作符,并被编译器完全优化。

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

例子来自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<=>

从概念上讲,定义平等并不容易。即使对于POD数据,有人可能会说,即使字段是相同的,但它是不同的对象(在不同的地址),它也不一定相等。这实际上取决于操作符的用法。不幸的是,你的编译器不是通灵的,不能推断。

除此之外,默认函数是搬起石头砸自己的脚的好方法。你描述的默认值基本上是为了保持与POD结构体的兼容性。然而,它们确实会造成足够多的破坏,让开发人员忘记它们或默认实现的语义。

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 example regarding this. Your correction is appreciated if I'm wrong.