具有不一致排序推导的三向比较运算符

Sil*_*cer 18 c++ qt spaceship-operator return-type-deduction c++20

前段时间我定义了我的第一个三向比较运算符。它比较了单一类型并取代了多个常规运算符。很棒的功能。然后我尝试实现一个类似的运算符来通过委托比较两个变体:

auto operator <=> (const QVariant& l, const QVariant& r)
{   
   switch (l.type())
   {
      case QMetaType::Int:
         return l.toInt() <=> r.toInt();
      case QMetaType::Double:
         return l.toDouble() <=> r.toDouble();
      default:
         throw;
   }
}
Run Code Online (Sandbox Code Playgroud)

这不能编译,我收到错误

自动返回类型的不一致扣除:“std::strong_ordering”然后是“std::partial_ordering”。

显然intdouble飞船运营商返回的类型不同。

解决这个问题的正确方法是什么?

Bar*_*rry 21

以相同的方式解析任何其他返回auto不同return语句推导出不同的函数。你要么:

  1. 确保所有的returns 都具有相同的类型,或者
  2. 显式选择返回类型。

在这种情况下,ints 比较为strong_orderingwhile doubles 比较为partial_ordering,并且strong_ordering可以隐式转换为partial_ordering,您可以执行以下任一操作:

std::partial_ordering operator <=>(const QVariant& l, const QVariant& r) {
    // rest as before
}
Run Code Online (Sandbox Code Playgroud)

或显式转换整数比较:

      case QMetaType::Int:
         return std::partial_ordering(l.toInt() <=> r.toInt());
Run Code Online (Sandbox Code Playgroud)

这为您提供了一个返回partial_ordering.


如果您想返回strong_ordering,则必须将double比较提升到更高的类别。您可以通过两种方式做到这一点:

您可以使用std::strong_order,这是一个更昂贵的操作,但提供所有浮点值的总排序。然后你会写:

      case QMetaType::Double:
         return std::strong_order(l.toDouble(), r.toDouble());
Run Code Online (Sandbox Code Playgroud)

或者你可以做一些像 think NaNs 格式错误的事情,然后以某种方式把它们扔掉:

      case QMetaType::Double: {
         auto c = l.toDouble() <=> r.toDouble();
         if (c == std::partial_ordering::unordered) {
             throw something;
         } else if (c == std::partial_ordering::less) {
            return std::strong_ordering::less;
         } else if (c == std::partial_ordering::equivalent) {
            return std::strong_ordering::equal;
         } else {
            return std::strong_ordering::greater;
         }
      }
Run Code Online (Sandbox Code Playgroud)

这更乏味,但我不确定是否有更直接的方法来进行这种提升。


Die*_*ühl 5

operator<=>forint和for的类型double不同,但它们应该有一个共同的类型。您可能希望利用编译器自动查找正确的类型。你可以这样做std::common_type,但那会很丑陋。利用std::common_type类型(在库而不是编译器中实现时)并使用三元运算符会更容易:

auto operator <=> (const QVariant& l, const QVariant& r)
{   
    return l.type() == QMetaType:Int? l.toInt() <=> r.toInt()
         : l.type() == QMetaType::Double? l.toDouble() <=> r.toDouble()
         : throw;
}
Run Code Online (Sandbox Code Playgroud)