簡體   English   中英

順序推導不一致的三向比較算子

[英]Three-way comparison operator with inconsistent ordering deduction

前段時間我定義了我的第一個三路比較運算符。 它比較了單一類型並替換了多個常規運算符。 很棒的功能。 然后我嘗試實現一個類似的運算符來通過委托比較兩個變體:

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;
   }
}

這不編譯,我得到錯誤

自動返回類型的扣除不一致:'std::strong_ordering' 然后是 'std::partial_ordering'。

顯然intdouble spaceship 運算符返回不同的類型。

解決這個問題的正確方法是什么?

以同樣的方式解決任何其他返回auto的 function ,其中不同的return語句推導不同。 你要么:

  1. 確保所有return具有相同的類型,或者
  2. 明確選擇返回類型。

在這種情況下, int s 比較為strong_orderingdouble s 比較為partial_ordering ,而strong_ordering可以隱式轉換為partial_ordering ,您可以執行以下任一操作:

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

或顯式轉換 integer 比較:

      case QMetaType::Int:
         return std::partial_ordering(l.toInt() <=> r.toInt());

這給了你一個 function 返回partial_ordering


如果您想返回strong_ordering ,則必須將double比較提升到更高的類別。 您可以通過兩種方式做到這一點:

您可以使用std::strong_order ,這是一個更昂貴的操作,但提供了對所有浮點值的總排序。 然后你會寫:

      case QMetaType::Double:
         return std::strong_order(l.toDouble(), r.toDouble());

或者你可以做一些事情,比如考慮NaN的格式錯誤並以某種方式將它們扔掉:

      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;
         }
      }

這更乏味,但我不確定是否有更直接的方法來進行這種提升。

intdoubleoperator<=>的類型不同,但它們應該具有共同的類型。 您可能希望利用編譯器自動查找正確的類型。 可以使用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;
}

我玩弄了一些模板代碼來實現 Dietmar Kühls 使用std::common_type的想法。 這是結果示例代碼:

template <typename CommonT, typename... ArgsT> requires (sizeof...(ArgsT) == 0)
inline CommonT variantSpaceshipHelper([[maybe_unused]] const QVariant& pLeft, [[maybe_unused]] const QVariant& pRight) noexcept
{
   std::terminate(); // Variant type does not match any of the given template types
}

template <typename CommonT, typename T, typename... ArgsT>
inline CommonT variantSpaceshipHelper(const QVariant& pLeft, const QVariant& pRight) noexcept
{
   if (pLeft.type() == static_cast<QVariant::Type>(qMetaTypeId<T>()))
   {
      return (pLeft.value<T>() <=> pRight.value<T>());
   }

   return variantSpaceshipHelper<CommonT, ArgsT...>(pLeft, pRight);
}

template <typename... ArgsT>
inline auto variantSpaceship(const QVariant& pLeft, const QVariant& pRight) noexcept
{
   using CommonT = std::common_type_t<decltype(std::declval<ArgsT>() <=> std::declval<ArgsT>())...>;
   return variantSpaceshipHelper<CommonT, ArgsT...>(pLeft, pRight);
}

inline auto operator <=>(const QVariant& pLeft, const QVariant& pRight) noexcept
{
   assert(pLeft.type() == pRight.type());
   return variantSpaceship<int, double>(pLeft, pRight);
}

可以輕松地將其他類型添加到variantSpaceship調用中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM