Three-way comparison operator with inconsistent ordering deduction
Same way you resolve any other function which returns auto
in which different return
statements deduce differently. You either:
- Ensure that all the
return
s have the same type, or - Explicitly pick a return type.
In this case, int
s compare as strong_ordering
while double
s compare as partial_ordering
, and strong_ordering
is implicitly convertible to partial_ordering
, you can do either:
std::partial_ordering operator <=>(const QVariant& l, const QVariant& r) {
// rest as before
}
or explicitly cast the integer comparison:
case QMetaType::Int:
return std::partial_ordering(l.toInt() <=> r.toInt());
That gives you a function returning partial_ordering
.
If you want to return strong_ordering
instead, you have to lift the double
comparison to a higher category. You can do that in two ways:
You can use std::strong_order
, which is a more expensive operation, but provides a total ordering over all floating point values. You would then write:
case QMetaType::Double:
return std::strong_order(l.toDouble(), r.toDouble());
Or you can do something like consider NaN
s ill-formed and throw them out somehow:
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;
}
}
It's more tedious but I'm not sure if there's a more direct way to do this kind of lifting.
The types of the operator<=>
for int
and double
differ but they should have a common type. You probably want to leverage the compiler in automatically finding the proper type. You could use std::common_type
to do but that would be quite ugly. It is easier to just leverage what std::common_type
type does under the (when implemented in the library rather the compiler) and use the ternary operator:
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;
}