三元比较运算符成员与非成员实现

Mar*_*ila 2 c++ comparison-operators spaceship-operator c++20

如果出现以下情况,双向比较运算符应为非成员函数:

  • 你希望第一个操作数是一个不是这个类的类型
  • 你想要隐式类型转换任何两个操作数

新的C++ 20三向比较运算符具有对称生成规则.表达式的名称查找a@b(其中@是双向比较运算符)按顺序完成a@b,a<=>b并且b<=>a(在从重载分辨率集中选择最佳匹配时,在出现歧义时具有此优先顺序).有关详细信息,请参见P0515R2.这意味着运算符<=>可以是成员函数,并且仍允许第一个操作数不属于此类类型.

然而,该文件包含这一说明:

通常,运算符<=>应该只是一个成员函数; 由于§2.3中的对称生成规则,您仍将获得每个参数的转换.在极少数情况下,您还希望同时支持两个参数的转换(为了能够比较两个对象,这两个对象都不是此类型,而是使用此类型的比较函数),请将其设为非成员朋友.

如果我理解正确,那么只有在需要同时对两个操作数进行隐式转换时才需要非成员实现?那是对的吗?如果需要,我能看到一个实际的例子吗?我正在考虑这个,虽然它似乎不是一个有效的例子:

struct foo
{
   foo(int const x) 
      : data{ x } {}
   foo(std::string_view x) 
      : data{std::stoi(x.data())}{}

   friend auto operator<=>(foo const & lhv, foo const & rhv) noexcept
   {
      return lhv.data <=> rhv.data;
   }

private:
   int data;
};


int main()
{
   assert(foo {42} == foo {"42"});        // OK
   assert(42 == std::string_view("42"));  // ??
}
Run Code Online (Sandbox Code Playgroud)

Bar*_*rry 5

这是一个说明性的(虽然不一定是实用的)示例:

struct A {
    int i;
};

struct B {
    B(A a) : i(a.i) { }

    int i;
};

strong_ordering operator<=>(B const& lhs, B const& rhs) {
    return lhs.i <=> rhs.i;
}

A{2} == A{2}; // okay, true
A{2} < A{1};  // okay, false
Run Code Online (Sandbox Code Playgroud)

我们发现候选人B在全球范围内取两个s,并且它是可行的,所以我们转换两个参数并使用它.如果该运算符是成员函数或friend在类中声明的非成员,则名称查找将找不到它.


请注意,在OP中,<=>声明为类中的非成员friend.这意味着名称查找不会找到它42 == string_view("42"),因为这些参数都不是foo.您需要添加一个普通的非成员声明,​​以使其对此类查找可见.