C++ 20 中的运算符 == 和 <=> 应该作为成员还是自由函数来实现?

Should the operators == and <=> in C++ 20 be implemented as a member or a free function?

注意:我认为这在技术上与 question 重复,但是:

  1. C++20 中对 == 的更改非常激进,我不确定是否 恢复9年的问题是正确的做法。
  2. 我特别询问了正在重写的运算符 ==<=> 编译器,不是例如运算符 <.

p.s。我现在有自己的看法(基于 foonathan 的一些谈话),但这只是当前的偏好,我不想用它来偏见潜在的答案。

我认为从软件工程的角度来看,如果可能的话,人们应该总是更喜欢使用自由函数而不是成员方法。我相信所有功能都是如此。为什么?它改进了封装并将函数从 "knowing" 如何实现 class 中解放出来。当然,比较函数通常需要访问私有成员,那么使用 friend 或成员函数就可以了(我仍然更喜欢 friend)。 Scott Meyers 在 Effective C++,第 23 项

中写了一些关于它的内容

Here is article by Scott that reiterates this thought

我认为在 C++20 中,比较应该是成员函数,除非你有一个非常令人信服的理由。

让我先从 C++17 微积分开始:我们经常将我们的比较写成非成员。这样做的原因是它是允许双向比较的唯一方法。如果我有一个类型 X,我想与 int 进行比较,我不能让 1 == X{} 与成员函数一起工作 - 它 成为自由函数:

struct X { };
bool operator==(X, int);
bool operator==(int lhs, X rhs) { return rhs == lhs; }

在这件事上没有太多选择。现在,将这些写成纯粹的自由函数是次优的,因为我们正在污染命名空间并增加查找中的候选人数量 - 所以最好让他们成为 hidden friends:

struct X {
    friend bool operator==(X, int);
    friend bool operator==(int lhs, X rhs) { return rhs == lhs; }
};

在 C++20 中,我们没有这个问题,因为比较本身是对称的。你可以只写:

struct X {
    bool operator==(int) const;
};

并且仅此声明就已经允许 X{} == 11 == X{},同时还没有为名称查找贡献额外的候选者(如果一方或另一方是X).

此外,在 C++20 中,如果比较是在 class 的声明中声明的,则可以使用默认比较。这些可以是成员函数或隐藏的好友,但不能是外部自由函数。


一个有趣的案例是我 运行 与 std::string 进行比较的原因。该类型的比较当前是非成员函数模板:

 template<class charT, class traits, class Allocator>
    constexpr bool
      operator==(const basic_string<charT, traits, Allocator>& lhs,
                 const basic_string<charT, traits, Allocator>& rhs) noexcept;

这与使它成为成员(非模板)函数或隐藏友元(非模板)函数具有重要的不同语义,因为它不允许通过作为模板进行隐式转换。作为 I pointed out,将此比较运算符转换为非模板会产生突然允许双方隐式转换的效果,这可能会破坏以前没有意识到这种可能性的代码。

但无论如何,如果您有一个 class 模板并希望避免在您的比较中进行转换,这可能是坚持为您的比较运算符使用非成员函数模板的一个很好的理由。但仅此而已。