【问题标题】:Three-way comparison operator member vs non-member implementation三向比较运算符成员与非成员实现
【发布时间】:2019-03-27 00:58:45
【问题描述】:

双向比较运算符应该是非成员函数,如果:

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

新的 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"));  // ??
}

【问题讨论】:

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


    【解决方案1】:

    这是一个说明性的(尽管不一定是实际的)示例:

    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
    

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


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

    【讨论】:

    • 感谢您的示例以及对声明操作符 方式的评论。我将示例更改为实际意图。
    • @Marius 抱歉,我恢复了您的编辑。请不要编辑问题以使答案无效。这个问题很好,强调这个问题很重要。
    • struct silly {silly(std::vector&lt;silly&gt; const&amp; v):s(v.size()) {}; std::size_t s = -1; friend std::strong_ordering operator&lt;=&gt;(silly const&amp; lhs, silly const&amp; rhs) {return lhs.s &lt;=&gt; rhs.s;}}; -- 即使两个参数都不是sillystd::vector&lt;silly&gt;{} &lt;=&gt; std::vector&lt;silly&gt;{} 仍然找到方法并转换!
    • @Yakk-AdamNevraumont 这是因为非默认的三路比较运算符 (&lt;=&gt;) 不会生成其他比较运算符。
    • @Yakk-AdamNevraumont 对不起,我以为你用== 举例。您需要对std::vector&lt;silly&gt; 进行部分模板特化。 godbolt.org/z/GjT8zhazc
    猜你喜欢
    • 2021-06-14
    • 2011-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-30
    • 2014-05-05
    • 1970-01-01
    相关资源
    最近更新 更多