Taking a look at cppreference
template<class T, class U, class Cat = std::partial_ordering>
concept three_way_comparable_with =
std::three_way_comparable<T, Cat> &&
std::three_way_comparable<U, Cat> &&
std::common_reference_with<
const std::remove_reference_t<T>&,
const std::remove_reference_t<U>&> &&
std::three_way_comparable<
std::common_reference_t<
const std::remove_reference_t<T>&,
const std::remove_reference_t<U>&>, Cat> &&
__WeaklyEqualityComparableWith<T, U> &&
__PartiallyOrderedWith<T, U> &&
requires(const std::remove_reference_t<T>& t,
const std::remove_reference_t<U>& u) {
{ t <=> u } -> __ComparesAs<Cat>;
{ u <=> t } -> __ComparesAs<Cat>;
};
In all comparison concepts, the operands (t
and u
here) are always const-qualified references.
This means that if I have an "unorthodox" class
struct S {
int i;
auto operator<=>(const S&) const = default;
void* operator<=>(S&) = delete;
};
then S
models std::three_way_comparable
but the following fails
S a, b;
a <=> b;
Does this mean that I must enforce constness for comparisons in any generic code?
template<typename T, typename U>
requires std::three_way_comparable_with<T, U>
auto foo(T&& t, U&& u)
{
// if(t <=> u > 0) // wrong!
if(static_cast<const std::remove_reference_t<T>&>(t) <=>
static_cast<const std::remove_reference_t<U>&>(u) > 0)
return bar(std::forward<T>(t));
else
return baz(std::forward<U>(u));
}
CodePudding user response:
As hinted by @StoryTeller in the comments:
According to the "implicit expression variations" in [concepts.equality]/6, because the expression t <=> u
is non-modifying (the operands are const
lvalue references), variations of the expression expecting non-const
lvalue references instead of const
lvalue references are implicitly also required. However, it is unspecified whether these requirements are validated.
I don't see any exceptions to this rule for std::three_way_comparable_with
in [cmp.concept].
Your type S
violates this additional requirement because the variation
requires(std::remove_reference_t<T>& t,
std::remove_reference_t<U>& u) {
{ t <=> u } -> __ComparesAs<Cat>;
{ u <=> t } -> __ComparesAs<Cat>;
};
is not satisfied. It therefore doesn't model std::three_way_comparable_with
.
There is even a very similar example under the referenced clause.