Постоянство и понятия сравнения в универсальном коде
Взглянем на 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>;
};
Во всех концепциях сравнения операнды (
t
а также
u
здесь) всегда являются константными ссылками.
Это означает, что если у меня есть "неортодоксальный" класс
struct S {
int i;
auto operator<=>(const S&) const = default;
void* operator<=>(S&) = delete;
};
тогда
S
модели
std::three_way_comparable
но следующее не удается
S a, b;
a <=> b;
Означает ли это, что я должен обеспечить постоянство для сравнений в любом универсальном коде ?
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));
}
1 ответ
Как намекнул @StoryTeller в комментариях:
Согласно « вариациям неявных выражений » в [concepts.equality]/6, поскольку выражение
t <=> u
не модифицирует (операнды являются ссылками lvalue), вариации выражения ожидают ссылки не lvalue вместо
const
Ссылки lvalue также требуются неявно. Однако не уточняется, подтверждены ли эти требования.
Я не вижу никаких исключений из этого правила для [cmp.concept].
Твой тип
S
нарушает это дополнительное требование, поскольку вариация
requires(std::remove_reference_t<T>& t,
std::remove_reference_t<U>& u) {
{ t <=> u } -> __ComparesAs<Cat>;
{ u <=> t } -> __ComparesAs<Cat>;
};
не удовлетворен. Поэтому он не моделирует
std::three_way_comparable_with
.
Есть даже очень похожий пример в указанном пункте.