通用代码中的常量和比较概念

Constness and comparison concepts in generic code

正在看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>;
  };

在所有的比较概念中,操作数(这里的tu)总是const限定的引用。

这意味着如果我有一个“非正统”class

struct S {
    int i;
    auto operator<=>(const S&) const = default;
    void* operator<=>(S&) = delete;
};

然后 S 模型 std::three_way_comparable 但以下失败

S a, b;
a <=> b;

这是否意味着我必须在 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));
}

正如@StoryTeller 在评论中所暗示的那样:

根据[concepts.equality]/6中的“隐式表达式变体”,因为表达式t <=> u是non-modifying(操作数是const 左值引用),期望非 const 左值引用而不是 const 左值引用的表达式的变体也是隐式需要的。但是,未指定这些要求是否得到验证。

我没有看到 [cmp.concept]std::three_way_comparable_with 的此规则有任何例外。

您的类型 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.

建模

引用的子句下还有一个very similar example