Постоянство и понятия сравнения в универсальном коде

Взглянем на 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.

Есть даже очень похожий пример в указанном пункте.

Другие вопросы по тегам