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

时间:2018-10-22 21:30:25

标签: c++ comparison-operators c++20 spaceship-operator

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

  • 您希望第一个操作数的类型不是此类
  • 您要隐式转换两个操作数中的任何一个

新的C ++ 20三向比较运算符具有对称的生成规则。表达式a@b(其中@是双向比较运算符)的名称查找按a@ba<=>bb<=>a的顺序进行(用从过载解析度集合中选择最佳匹配时,如果模棱两可,则采用这种优先顺序。有关详情,请参见P0515R2。这意味着运算符<=>可以是成员函数,并且仍然允许第一个操作数不属于此类。

但是,本文包含以下注释:

  

通常,operator <=>应该只是成员函数;你仍然会   由于对称生成,因此每个参数都可获得转换   §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"));  // ??
}

1 个答案:

答案 0 :(得分: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

我们发现候选人在全球范围内接受了两个B,这是可行的,因此我们将两个参数都转换并使用它。如果该运算符是在类中声明的成员函数或非成员friend,则名称查找将找不到它。


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