ranges::sort 不编译

ranges::sort does not compile

在MyRect.h中:

struct MyRect
{
    MyRect(std::initializer_list<int> i);
    MyRect();
    int16_t m_left=0, m_right=0, m_top=0, m_bottom=0 ;
    int16_t no_sequence=0 ;
    int16_t i=-1 ;
    bool selected=false ;
} ;

bool operator==(const MyRect& r1, const MyRect& r2) ;
bool operator<(const MyRect& r1, const MyRect& r2);

在MyRect.cpp中:

bool operator==(const MyRect& r1, const MyRect& r2)
{
    return r1.m_left==r2.m_left &&
        r1.m_right==r2.m_right &&
        r1.m_top==r2.m_top &&
        r1.m_bottom==r2.m_bottom ;
}
bool operator<(const MyRect& r1, const MyRect& r2)
{
    if (r1.m_left != r2.m_left)
        return r1.m_left < r2.m_left;
    if (r1.m_right != r2.m_right)
        return r1.m_right < r2.m_right;
    if (r1.m_top != r2.m_top)
        return r1.m_top < r2.m_top;
    if (r1.m_bottom != r2.m_bottom)
        return r1.m_bottom < r2.m_bottom;
    //if we got here, r1==r2
    return false;
}

在binpack.cpp中:

#include "MyRect.h"
...
vector<MyRect> selected_neighboors ;
std::sort(selected_neighboors.begin(), selected_neighboors.end()) ;

使用 g++10 (C++20) binpack.cpp 编译。没问题。

如果我改成:

ranges::sort(selected_neighboors);

它不再编译。

/home/edouda/linkedboxdraw/binpack.cpp: In function ‘void collapse(std::vector<MyRect>&)’:
/home/edouda/linkedboxdraw/binpack.cpp:617:36: error: no match for call to ‘(const std::ranges::__sort_fn) (std::vector<MyRect>&)’
  617 |    ranges::sort(selected_neighboors) ;
      |                                    ^
In file included from /usr/include/c++/10/algorithm:64,
                 from /home/edouda/linkedboxdraw/MyRect.h:14,
                 from /home/edouda/linkedboxdraw/binpack.h:12,
                 from /home/edouda/linkedboxdraw/binpack.cpp:8:
/usr/include/c++/10/bits/ranges_algo.h:2019:7: note: candidate: ‘template<class _Iter, class _Sent, class _Comp, class _Proj>  requires (random_access_iterator<_Iter>) && (sentinel_for<_Sent, _Iter>) && (sortable<_Iter, _Comp, _Proj>) constexpr _Iter std::ranges::__sort_fn::operator()(_Iter, _Sent, _Comp, _Proj) const’
 2019 |       operator()(_Iter __first, _Sent __last,
      |       ^~~~~~~~
/usr/include/c++/10/bits/ranges_algo.h:2019:7: note:   template argument deduction/substitution failed:
/home/edouda/linkedboxdraw/binpack.cpp:617:36: note:   candidate expects 4 arguments, 1 provided
  617 |    ranges::sort(selected_neighboors) ;
      |                                    ^
In file included from /usr/include/c++/10/algorithm:64,
                 from /home/edouda/linkedboxdraw/MyRect.h:14,
                 from /home/edouda/linkedboxdraw/binpack.h:12,
                 from /home/edouda/linkedboxdraw/binpack.cpp:8:
/usr/include/c++/10/bits/ranges_algo.h:2032:7: note: candidate: ‘constexpr std::ranges::borrowed_iterator_t<_Range> std::ranges::__sort_fn::operator()(_Range&&, _Comp, _Proj) const [with _Range = std::vector<MyRect>&; _Comp = std::ranges::less; _Proj = std::identity; std::ranges::borrowed_iterator_t<_Range> = std::conditional<true, __gnu_cxx::__normal_iterator<MyRect*, std::vector<MyRect> >, std::ranges::dangling>::type]’
 2032 |       operator()(_Range&& __r, _Comp __comp = {}, _Proj __proj = {}) const
      |       ^~~~~~~~
/usr/include/c++/10/bits/ranges_algo.h:2032:7: note: constraints not satisfied
In file included from /usr/include/c++/10/compare:39,
                 from /usr/include/c++/10/bits/stl_pair.h:65,
                 from /usr/include/c++/10/bits/stl_algobase.h:64,
                 from /usr/include/c++/10/vector:60,
                 from /home/edouda/linkedboxdraw/MyRect.h:12,
                 from /home/edouda/linkedboxdraw/binpack.h:12,
                 from /home/edouda/linkedboxdraw/binpack.cpp:8:
/usr/include/c++/10/concepts: In instantiation of ‘constexpr std::ranges::borrowed_iterator_t<_Range> std::ranges::__sort_fn::operator()(_Range&&, _Comp, _Proj) const [with _Range = std::vector<MyRect>&; _Comp = std::ranges::less; _Proj = std::identity; std::ranges::borrowed_iterator_t<_Range> = std::conditional<true, __gnu_cxx::__normal_iterator<MyRect*, std::vector<MyRect> >, std::ranges::dangling>::type]’:
/home/edouda/linkedboxdraw/binpack.cpp:617:36:   required from here
/usr/include/c++/10/concepts:338:13:   required for the satisfaction of ‘invocable<_Fn, _Args ...>’ [with _Fn = std::ranges::less&; _Args = {value_type<MyRect>&, value_type<MyRect>&}]
/usr/include/c++/10/concepts:342:13:   required for the satisfaction of ‘regular_invocable<_Fn, _Args ...>’ [with _Fn = std::ranges::less&; _Args = {value_type<MyRect>&, value_type<MyRect>&}]
/usr/include/c++/10/concepts:346:13:   required for the satisfaction of ‘predicate<_Rel, _Tp, _Tp>’ [with _Rel = std::ranges::less&; _Tp = MyRect&]
/usr/include/c++/10/concepts:351:13:   required for the satisfaction of ‘relation<_Rel, _Tp, _Up>’ [with _Rel = std::ranges::less&; _Tp = MyRect&; _Up = MyRect&]
/usr/include/c++/10/concepts:361:13:   required for the satisfaction of ‘strict_weak_order<_Fn&, typename std::__detail::__iter_traits_impl<typename std::remove_cv<typename std::remove_reference<_Arg>::type>::type, std::indirectly_readable_traits<typename std::remove_cv<typename std::remove_reference<_Arg>::type>::type> >::type::value_type&, typename std::__detail::__iter_traits_impl<typename std::remove_cv<typename std::remove_reference<_Arg>::type>::type, std::indirectly_readable_traits<typename std::remove_cv<typename std::remove_reference<_Arg>::type>::type> >::type::value_type&>’ [with _Fn = std::ranges::less; _Arg = std::projected<__gnu_cxx::__normal_iterator<MyRect*, std::vector<MyRect, std::allocator<MyRect> > >, std::identity>; _Arg = std::projected<__gnu_cxx::__normal_iterator<MyRect*, std::vector<MyRect, std::allocator<MyRect> > >, std::identity>]
/usr/include/c++/10/bits/iterator_concepts.h:690:13:   required for the satisfaction of ‘indirect_strict_weak_order<_Rel, std::projected<_Iter, _Proj>, std::projected<_Iter, _Proj> >’ [with _Rel = std::ranges::less; _Iter = __gnu_cxx::__normal_iterator<MyRect*, std::vector<MyRect, std::allocator<MyRect> > >; _Proj = std::identity]
/usr/include/c++/10/bits/iterator_concepts.h:865:13:   required for the satisfaction of ‘sortable<decltype (std::__detail::__ranges_begin(declval<_Container&>())), _Comp, _Proj>’ [with _Container = std::vector<MyRect, std::allocator<MyRect> >&; _Comp = std::ranges::less; _Proj = std::identity]
/usr/include/c++/10/concepts:338:25: note: the expression ‘is_invocable_v<_Fn, _Args ...> [with _Fn = std::ranges::less&; _Args = {value_type<MyRect>&, value_type<MyRect>&}]’ evaluated to ‘false’
  338 |     concept invocable = is_invocable_v<_Fn, _Args...>;
      |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
make[2]: *** [CMakeFiles/latuile.dir/build.make:76: CMakeFiles/latuile.dir/binpack.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:78: CMakeFiles/latuile.dir/all] Error 2
make: *** [Makefile:95: all] Error 2

ranges::sort默认使用ranges::less来比较MyRect,定义在[range.cmp]:

struct ranges::less {
  template<class T, class U>
    constexpr bool operator()(T&& t, U&& u) const;

  using is_transparent = unspecified;
};
template<class T, class U>
  constexpr bool operator()(T&& t, U&& u) const;

Constraints: T and U satisfy totally_­ordered_­with.

operator()要求TU必须满足total_ordered_with:

template<class T, class U>
  concept totally_­ordered_­with =
    totally_­ordered<T> && totally_­ordered<U> && ...

要求T必须满足totally_ordered:

template<class T>
  concept totally_­ordered =
    equality_­comparable<T> && partially-ordered-with<T, T>;

要求T必须满足partially-ordered-with:

template<class T, class U>
  concept partially-ordered-with =      // exposition only
    requires(const remove_reference_t<T>& t, const remove_reference_t<U>& u) {
      { t <  u } -> boolean-testable;
      { t >  u } -> boolean-testable;
      { t <= u } -> boolean-testable;
      { t >= u } -> boolean-testable;
      { u <  t } -> boolean-testable;
      { u >  t } -> boolean-testable;
      { u <= t } -> boolean-testable;
      { u >= t } -> boolean-testable;
    };

这要求完整的关系运算符集必须是 well-formed。

由于您没有为MyRect定义合适的operator>operator<=等关系运算符,不满足约束条件。

你可以在MyRect上加上operator<=>使之成为totally_ordered,或者使用无约束的std::less进行比较:

std::vector<MyRect> selected_neighboors;
ranges::sort(selected_neighboors, std::less{});