[英]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'。
顯然int
和double
spaceship 運算符返回不同的類型。
解決這個問題的正確方法是什么?
以同樣的方式解決任何其他返回auto
的 function ,其中不同的return
語句推導不同。 你要么:
return
具有相同的類型,或者 在這種情況下, int
s 比較為strong_ordering
而double
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;
}
}
這更乏味,但我不確定是否有更直接的方法來進行這種提升。
int
和double
的operator<=>
的類型不同,但它們應該具有共同的類型。 您可能希望利用編譯器自動查找正確的類型。 你可以使用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.