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

默认(空)构造函数 复制构造函数 一个析构函数 赋值运算符(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
{ }

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


当前回答

在这个视频中,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 assignment but that wouldn't work for ==. A more detailed explanation can be found in this article from Bjarne Stroustrup. In the follow up question Why then wasn't a member by member comparison used he 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!

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

其他回答

如果编译器可以提供默认的复制构造函数,它就应该能够提供类似的默认操作符==(),这在一定程度上是有意义的。我认为决定不为该操作符提供编译器生成的默认构造函数的原因可以从Stroustrup在“c++的设计和发展”(第11.4.1节-复制的控制)中关于默认复制构造函数的描述中猜到:

我个人认为这很不幸 定义的复制操作 默认和我禁止复制 我的许多类的对象。 然而,c++继承了它的默认值 赋值和复制构造函数 C,它们经常被使用。

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

出于我自己的目的,在我的IDE中,我用于新类的代码片段包含私有赋值操作符和复制构造函数的声明,因此当我生成一个新类时,我没有得到默认的赋值和复制操作——如果我想让编译器能够为我生成这些操作,我必须显式地从private:节中删除这些操作的声明。

c++ 0x有一个默认函数的提议,所以你可以说default operator==; 我们已经了解到,把这些事情弄清楚是有帮助的。

定义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的用法。

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

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

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

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

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

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

但是自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;
};

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

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

这个特定的特性(默认操作符==以及==和!=之间的对称)来自一个提议,该提议是更广泛的语言特性操作符<=>的一部分。