This adds the missing parts of P0896R4 to reverse_iterator and move_iterator, so that they meet the C++20 requirements. This should be the last piece of P0896R4, meaning ranges support is now complete.
The PR 94354 bug with reverse_iterator's comparisons is fixed for C++20 only, but that change should be extended to C++11, C++14 and C++17 modes in stage 1. * include/bits/stl_iterator.h (reverse_iterator::iterator_concept) (reverse_iterator::iterator_category): Define for C++20. (reverse_iterator): Define comparison operators correctly for C++20. (__normal_iterator): Add constraints to comparison operators for C++20. (move_iterator::operator++(int)) [__cpp_lib_concepts]: Define new overload for input iterators. (move_iterator): Add constraints to comparison operators for C++20. Define operator<=> for C++20. * testsuite/24_iterators/move_iterator/input_iterator.cc: New test. * testsuite/24_iterators/move_iterator/move_only.cc: New test. * testsuite/24_iterators/move_iterator/rel_ops_c++20.cc: New test. * testsuite/24_iterators/reverse_iterator/rel_ops_c++20.cc: New test. Tested powerpc64le-linux, committed to master.
commit 81a8d137c22953df2ea046466c62cd26c0dba103 Author: Jonathan Wakely <jwak...@redhat.com> Date: Fri Mar 27 23:21:58 2020 +0000 libstdc++: Add remaining C++20 changes to iterator adaptors This adds the missing parts of P0896R4 to reverse_iterator and move_iterator, so that they meet the C++20 requirements. This should be the last piece of P0896R4, meaning ranges support is now complete. The PR 94354 bug with reverse_iterator's comparisons is fixed for C++20 only, but that change should be extended to C++11, C++14 and C++17 modes in stage 1. * include/bits/stl_iterator.h (reverse_iterator::iterator_concept) (reverse_iterator::iterator_category): Define for C++20. (reverse_iterator): Define comparison operators correctly for C++20. (__normal_iterator): Add constraints to comparison operators for C++20. (move_iterator::operator++(int)) [__cpp_lib_concepts]: Define new overload for input iterators. (move_iterator): Add constraints to comparison operators for C++20. Define operator<=> for C++20. * testsuite/24_iterators/move_iterator/input_iterator.cc: New test. * testsuite/24_iterators/move_iterator/move_only.cc: New test. * testsuite/24_iterators/move_iterator/rel_ops_c++20.cc: New test. * testsuite/24_iterators/reverse_iterator/rel_ops_c++20.cc: New test. diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h index d10c30cbfcc..26eb599993d 100644 --- a/libstdc++-v3/include/bits/stl_iterator.h +++ b/libstdc++-v3/include/bits/stl_iterator.h @@ -88,6 +88,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @{ */ +#if __cplusplus > 201703L && __cpp_lib_concepts + namespace __detail + { + // Weaken iterator_category _Cat to _Limit if it is derived from that, + // otherwise use _Otherwise. + template<typename _Cat, typename _Limit, typename _Otherwise = _Cat> + using __clamp_iter_cat + = conditional_t<derived_from<_Cat, _Limit>, _Limit, _Otherwise>; + } +#endif + // 24.4.1 Reverse iterators /** * Bidirectional and random access iterators have corresponding reverse @@ -126,6 +137,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typedef typename __traits_type::pointer pointer; typedef typename __traits_type::reference reference; +#if __cplusplus > 201703L && __cpp_lib_concepts + using iterator_concept + = conditional_t<random_access_iterator<_Iterator>, + random_access_iterator_tag, + bidirectional_iterator_tag>; + using iterator_category + = __detail::__clamp_iter_cat<typename __traits_type::iterator_category, + random_access_iterator_tag>; +#endif + /** * The default constructor value-initializes member @p current. * If it is a pointer, that means it is zero-initialized. @@ -320,16 +341,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return __t.operator->(); } }; + // Used in unevaluated expressions to test for implicit conversion to bool. + namespace __detail { bool __convbool(bool); } + //@{ /** * @param __x A %reverse_iterator. * @param __y A %reverse_iterator. * @return A simple bool. * - * Reverse iterators forward many operations to their underlying base() - * iterators. Others are implemented in terms of one another. + * Reverse iterators forward comparisons to their underlying base() + * iterators. * */ +#if __cplusplus <= 201703L template<typename _Iterator> inline _GLIBCXX17_CONSTEXPR bool operator==(const reverse_iterator<_Iterator>& __x, @@ -403,6 +428,49 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION operator>=(const reverse_iterator<_IteratorL>& __x, const reverse_iterator<_IteratorR>& __y) { return !(__x < __y); } +#else // C++20 + template<typename _IteratorL, typename _IteratorR> + constexpr auto + operator==(const reverse_iterator<_IteratorL>& __x, + const reverse_iterator<_IteratorR>& __y) + -> decltype(__detail::__convbool(__x.base() == __y.base())) + { return __x.base() == __y.base(); } + + template<typename _IteratorL, typename _IteratorR> + constexpr auto + operator!=(const reverse_iterator<_IteratorL>& __x, + const reverse_iterator<_IteratorR>& __y) + -> decltype(__detail::__convbool(__x.base() != __y.base())) + { return __x.base() != __y.base(); } + + template<typename _IteratorL, typename _IteratorR> + constexpr auto + operator<(const reverse_iterator<_IteratorL>& __x, + const reverse_iterator<_IteratorR>& __y) + -> decltype(__detail::__convbool(__x.base() < __y.base())) + { return __x.base() < __y.base(); } + + template<typename _IteratorL, typename _IteratorR> + constexpr auto + operator>(const reverse_iterator<_IteratorL>& __x, + const reverse_iterator<_IteratorR>& __y) + -> decltype(__detail::__convbool(__x.base() > __y.base())) + { return __x.base() > __y.base(); } + + template<typename _IteratorL, typename _IteratorR> + constexpr auto + operator<=(const reverse_iterator<_IteratorL>& __x, + const reverse_iterator<_IteratorR>& __y) + -> decltype(__detail::__convbool(__x.base() <= __y.base())) + { return __x.base() <= __y.base(); } + + template<typename _IteratorL, typename _IteratorR> + constexpr auto + operator>=(const reverse_iterator<_IteratorL>& __x, + const reverse_iterator<_IteratorR>& __y) + -> decltype(__detail::__convbool(__x.base() >= __y.base())) + { return __x.base() >= __y.base(); } +#endif // C++20 //@} #if __cplusplus < 201103L @@ -1000,8 +1068,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Random access iterator requirements template<typename _IteratorL, typename _IteratorR, typename _Container> - _GLIBCXX20_CONSTEXPR +#if __cplusplus > 201703L + constexpr auto +#else inline bool +#endif operator<(const __normal_iterator<_IteratorL, _Container>& __lhs, const __normal_iterator<_IteratorR, _Container>& __rhs) _GLIBCXX_NOEXCEPT @@ -1016,8 +1087,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return __lhs.base() < __rhs.base(); } template<typename _IteratorL, typename _IteratorR, typename _Container> - _GLIBCXX20_CONSTEXPR +#if __cplusplus > 201703L + constexpr auto +#else inline bool +#endif operator>(const __normal_iterator<_IteratorL, _Container>& __lhs, const __normal_iterator<_IteratorR, _Container>& __rhs) _GLIBCXX_NOEXCEPT @@ -1032,8 +1106,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return __lhs.base() > __rhs.base(); } template<typename _IteratorL, typename _IteratorR, typename _Container> - _GLIBCXX20_CONSTEXPR +#if __cplusplus > 201703L + constexpr auto +#else inline bool +#endif operator<=(const __normal_iterator<_IteratorL, _Container>& __lhs, const __normal_iterator<_IteratorR, _Container>& __rhs) _GLIBCXX_NOEXCEPT @@ -1048,8 +1125,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return __lhs.base() <= __rhs.base(); } template<typename _IteratorL, typename _IteratorR, typename _Container> - _GLIBCXX20_CONSTEXPR +#if __cplusplus > 201703L + constexpr auto +#else inline bool +#endif operator>=(const __normal_iterator<_IteratorL, _Container>& __lhs, const __normal_iterator<_IteratorR, _Container>& __rhs) _GLIBCXX_NOEXCEPT @@ -1157,15 +1237,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION private: _Sent _M_last; }; - - namespace __detail - { - // Weaken iterator_category _Cat to _Limit if it is derived from that, - // otherwise use _Otherwise. - template<typename _Cat, typename _Limit, typename _Otherwise = _Cat> - using __clamp_iter_cat - = conditional_t<derived_from<_Cat, _Limit>, _Limit, _Otherwise>; - } #endif // C++20 // 24.4.3 Move iterators @@ -1266,6 +1337,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __tmp; } +#if __cpp_lib_concepts + constexpr void + operator++(int) requires (!forward_iterator<_Iterator>) + { ++_M_current; } +#endif + _GLIBCXX17_CONSTEXPR move_iterator& operator--() { @@ -1343,6 +1420,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION inline _GLIBCXX17_CONSTEXPR bool operator==(const move_iterator<_IteratorL>& __x, const move_iterator<_IteratorR>& __y) +#if __cplusplus > 201703L && __cpp_lib_concepts + requires requires { __detail::__convbool(__x.base() == __y.base()); } +#endif { return __x.base() == __y.base(); } template<typename _Iterator> @@ -1351,6 +1431,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION const move_iterator<_Iterator>& __y) { return __x.base() == __y.base(); } +#if __cpp_lib_three_way_comparison + template<typename _IteratorL, + three_way_comparable_with<_IteratorL> _IteratorR> + constexpr compare_three_way_result_t<_IteratorL, _IteratorR> + operator<=>(const move_iterator<_IteratorL>& __x, + const move_iterator<_IteratorR>& __y) + { return __x.base() <=> __y.base(); } +#else template<typename _IteratorL, typename _IteratorR> inline _GLIBCXX17_CONSTEXPR bool operator!=(const move_iterator<_IteratorL>& __x, @@ -1362,11 +1450,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION operator!=(const move_iterator<_Iterator>& __x, const move_iterator<_Iterator>& __y) { return !(__x == __y); } +#endif template<typename _IteratorL, typename _IteratorR> inline _GLIBCXX17_CONSTEXPR bool operator<(const move_iterator<_IteratorL>& __x, const move_iterator<_IteratorR>& __y) +#if __cplusplus > 201703L && __cpp_lib_concepts + requires requires { __detail::__convbool(__x.base() < __y.base()); } +#endif { return __x.base() < __y.base(); } template<typename _Iterator> @@ -1379,6 +1471,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION inline _GLIBCXX17_CONSTEXPR bool operator<=(const move_iterator<_IteratorL>& __x, const move_iterator<_IteratorR>& __y) +#if __cplusplus > 201703L && __cpp_lib_concepts + requires requires { __detail::__convbool(__y.base() < __x.base()); } +#endif { return !(__y < __x); } template<typename _Iterator> @@ -1391,6 +1486,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION inline _GLIBCXX17_CONSTEXPR bool operator>(const move_iterator<_IteratorL>& __x, const move_iterator<_IteratorR>& __y) +#if __cplusplus > 201703L && __cpp_lib_concepts + requires requires { __detail::__convbool(__y.base() < __x.base()); } +#endif { return __y < __x; } template<typename _Iterator> @@ -1403,6 +1501,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION inline _GLIBCXX17_CONSTEXPR bool operator>=(const move_iterator<_IteratorL>& __x, const move_iterator<_IteratorR>& __y) +#if __cplusplus > 201703L && __cpp_lib_concepts + requires requires { __detail::__convbool(__x.base() < __y.base()); } +#endif { return !(__x < __y); } template<typename _Iterator> diff --git a/libstdc++-v3/testsuite/24_iterators/move_iterator/input_iterator.cc b/libstdc++-v3/testsuite/24_iterators/move_iterator/input_iterator.cc new file mode 100644 index 00000000000..32cf55ca932 --- /dev/null +++ b/libstdc++-v3/testsuite/24_iterators/move_iterator/input_iterator.cc @@ -0,0 +1,42 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include <iterator> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> + +void +test01() +{ + int a[2] = { 1, 2 }; + __gnu_test::test_container<int, __gnu_test::input_iterator_wrapper> c(a); + auto miter = std::make_move_iterator(c.begin()); + VERIFY( *miter == 1 ); + miter++; + VERIFY( *miter == 2 ); + + static_assert( std::is_void_v<decltype(miter++)> ); +} + +int +main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/24_iterators/move_iterator/move_only.cc b/libstdc++-v3/testsuite/24_iterators/move_iterator/move_only.cc new file mode 100644 index 00000000000..d64e61e4448 --- /dev/null +++ b/libstdc++-v3/testsuite/24_iterators/move_iterator/move_only.cc @@ -0,0 +1,60 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <iterator> + +struct move_only_iterator +{ + move_only_iterator() = default; + move_only_iterator(move_only_iterator&&) = default; + move_only_iterator& operator=(move_only_iterator&&) = default; + + move_only_iterator& operator++(); + move_only_iterator operator++(int); + int& operator*() const; + + bool operator==(const move_only_iterator&) const; +}; + +template<> struct std::iterator_traits<move_only_iterator> +{ + using value_type = int; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; +}; + +static_assert(std::input_iterator<move_only_iterator>); + +template<typename T> + concept has_member_base = requires (T t) { std::forward<T>(t).base(); }; + +static_assert( ! has_member_base<std::move_iterator<move_iterator>&> ); +static_assert( ! has_member_base<const std::move_iterator<move_iterator>&> ); +static_assert( has_member_base<std::move_iterator<move_iterator>> ); +static_assert( ! has_member_base<const std::move_iterator<move_iterator>> ); + +void +test01() +{ + std::move_iterator<move_only_iterator> m1, m2; + m1 = std::make_move_iterator(move_only_iterator{}); + m2 = std::move(m1); + m1.swap(m2); +} diff --git a/libstdc++-v3/testsuite/24_iterators/move_iterator/rel_ops_c++20.cc b/libstdc++-v3/testsuite/24_iterators/move_iterator/rel_ops_c++20.cc new file mode 100644 index 00000000000..8f2d73c520f --- /dev/null +++ b/libstdc++-v3/testsuite/24_iterators/move_iterator/rel_ops_c++20.cc @@ -0,0 +1,134 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <iterator> + +template<int> +struct Iter +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = int; + using pointer = int*; + using reference = int&; + using difference_type = std::ptrdiff_t; + + Iter(); + + Iter& operator++(); + Iter operator++(int); + Iter& operator--(); + Iter operator--(int); + int& operator*() const; + int* operator->() const; + + int& operator[](difference_type) const; + + Iter& operator+=(difference_type); + Iter& operator-=(difference_type); + + template<int N> friend Iter operator+(Iter<N>, difference_type); + template<int N> friend Iter operator+(difference_type, Iter<N>); + template<int N> friend Iter operator-(Iter<N>, difference_type); + template<int N> friend difference_type operator-(Iter<N>, Iter<N>); + + // Define the full set of operators for same-type comparisons + template<int N> friend bool operator==(Iter<N>, Iter<N>); // synthesizes != + template<int N> friend bool operator<(Iter<N>, Iter<N>); + template<int N> friend bool operator>(Iter<N>, Iter<N>); + template<int N> friend bool operator<=(Iter<N>, Iter<N>); + template<int N> friend bool operator>=(Iter<N>, Iter<N>); +}; + + +static_assert( std::random_access_iterator<Iter<0>> ); + +int operator==(Iter<0>, long*); +void* operator< (Iter<1>, long*); +bool& operator< (long*, Iter<2>); + +using std::move_iterator; + +static_assert( std::three_way_comparable<move_iterator<Iter<0>>> ); + +move_iterator<Iter<0>> l0{Iter<0>()}; +move_iterator<Iter<1>> l1{Iter<1>()}; +move_iterator<Iter<2>> l2{Iter<2>()}; +move_iterator<long*> r{nullptr}; + +bool b0 = l0 == r; +bool b1 = l0 != r; +bool b2 = l1 < r; +bool b3 = l2 > r; +bool b4 = l2 <= r; +bool b5 = l1 >= r; + +template<int N> + concept has_eq + = requires (move_iterator<Iter<N>> l, move_iterator<long*> r) + { l == r; }; + +template<int N> + concept has_ne + = requires (move_iterator<Iter<N>> l, move_iterator<long*> r) + { l != r; }; + +template<int N> + concept has_lt + = requires (move_iterator<Iter<N>> l, move_iterator<long*> r) + { l < r; }; + +template<int N> + concept has_gt + = requires (move_iterator<Iter<N>> l, move_iterator<long*> r) + { l > r; }; + +template<int N> + concept has_le + = requires (move_iterator<Iter<N>> l, move_iterator<long*> r) + { l <= r; }; + +template<int N> + concept has_ge + = requires (move_iterator<Iter<N>> l, move_iterator<long*> r) + { l >= r; }; + +static_assert( has_eq<0> ); +static_assert( ! has_eq<1> ); +static_assert( ! has_eq<2> ); + +static_assert( has_ne<0> ); // uses synthesized operator!= +static_assert( ! has_ne<1> ); +static_assert( ! has_ne<2> ); + +static_assert( ! has_lt<0> ); +static_assert( has_lt<1> ); +static_assert( ! has_lt<2> ); + +static_assert( ! has_gt<0> ); +static_assert( ! has_gt<1> ); +static_assert( has_gt<2> ); + +static_assert( ! has_le<0> ); +static_assert( ! has_le<1> ); +static_assert( has_le<2> ); + +static_assert( ! has_ge<0> ); +static_assert( has_ge<1> ); +static_assert( ! has_ge<2> ); diff --git a/libstdc++-v3/testsuite/24_iterators/reverse_iterator/rel_ops_c++20.cc b/libstdc++-v3/testsuite/24_iterators/reverse_iterator/rel_ops_c++20.cc new file mode 100644 index 00000000000..3e91a0396fe --- /dev/null +++ b/libstdc++-v3/testsuite/24_iterators/reverse_iterator/rel_ops_c++20.cc @@ -0,0 +1,156 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <iterator> + +template<int> +struct Iter +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = int; + using pointer = int*; + using reference = int&; + using difference_type = std::ptrdiff_t; + + Iter(); + + Iter& operator++(); + Iter operator++(int); + Iter& operator--(); + Iter operator--(int); + int& operator*() const; + int* operator->() const; + + int& operator[](difference_type) const; + + Iter& operator+=(difference_type); + Iter& operator-=(difference_type); + + template<int N> friend Iter operator+(Iter<N>, difference_type); + template<int N> friend Iter operator+(difference_type, Iter<N>); + template<int N> friend Iter operator-(Iter<N>, difference_type); + template<int N> friend difference_type operator-(Iter<N>, Iter<N>); + + // Define the full set of operators for same-type comparisons + template<int N> friend bool operator==(Iter<N>, Iter<N>); // synthesizes != + template<int N> friend bool operator<(Iter<N>, Iter<N>); + template<int N> friend bool operator>(Iter<N>, Iter<N>); + template<int N> friend bool operator<=(Iter<N>, Iter<N>); + template<int N> friend bool operator>=(Iter<N>, Iter<N>); +}; + +static_assert( std::random_access_iterator<Iter<0>> ); + +// Define a single kind of mixed-type comparison for each specialization. +int operator==(Iter<0>, long*); +void* operator!=(Iter<1>, long*); +bool& operator< (Iter<2>, long*); +int operator> (Iter<3>, long*); +void* operator<=(Iter<4>, long*); +bool& operator>=(Iter<5>, long*); + +using std::reverse_iterator; + +reverse_iterator<Iter<0>> l0{Iter<0>()}; +reverse_iterator<Iter<1>> l1{Iter<1>()}; +reverse_iterator<Iter<2>> l2{Iter<2>()}; +reverse_iterator<Iter<3>> l3{Iter<3>()}; +reverse_iterator<Iter<4>> l4{Iter<4>()}; +reverse_iterator<Iter<5>> l5{Iter<5>()}; +reverse_iterator<long*> r{nullptr}; + +bool b0 = l0 == r; +bool b1 = l1 != r; +bool b2 = l2 < r; +bool b3 = l3 > r; +bool b4 = l4 <= r; +bool b5 = l5 >= r; + +template<int N> + concept has_eq + = requires (reverse_iterator<Iter<N>> l, reverse_iterator<long*> r) + { l == r; }; + +template<int N> + concept has_ne + = requires (reverse_iterator<Iter<N>> l, reverse_iterator<long*> r) + { l != r; }; + +template<int N> + concept has_lt + = requires (reverse_iterator<Iter<N>> l, reverse_iterator<long*> r) + { l < r; }; + +template<int N> + concept has_gt + = requires (reverse_iterator<Iter<N>> l, reverse_iterator<long*> r) + { l > r; }; + +template<int N> + concept has_le + = requires (reverse_iterator<Iter<N>> l, reverse_iterator<long*> r) + { l <= r; }; + +template<int N> + concept has_ge + = requires (reverse_iterator<Iter<N>> l, reverse_iterator<long*> r) + { l >= r; }; + +static_assert( has_eq<0> ); +static_assert( ! has_eq<1> ); +static_assert( ! has_eq<2> ); +static_assert( ! has_eq<3> ); +static_assert( ! has_eq<4> ); +static_assert( ! has_eq<5> ); + +static_assert( has_ne<0> ); // uses synthesized operator!= +static_assert( has_ne<1> ); +static_assert( ! has_ne<2> ); +static_assert( ! has_ne<3> ); +static_assert( ! has_ne<4> ); +static_assert( ! has_ne<5> ); + +static_assert( ! has_lt<0> ); +static_assert( ! has_lt<1> ); +static_assert( has_lt<2> ); +static_assert( ! has_lt<3> ); +static_assert( ! has_lt<4> ); +static_assert( ! has_lt<5> ); + +static_assert( ! has_gt<0> ); +static_assert( ! has_gt<1> ); +static_assert( ! has_gt<2> ); +static_assert( has_gt<3> ); +static_assert( ! has_gt<4> ); +static_assert( ! has_gt<5> ); + +static_assert( ! has_le<0> ); +static_assert( ! has_le<1> ); +static_assert( ! has_le<2> ); +static_assert( ! has_le<3> ); +static_assert( has_le<4> ); +static_assert( ! has_le<5> ); + +static_assert( ! has_ge<0> ); +static_assert( ! has_ge<1> ); +static_assert( ! has_ge<2> ); +static_assert( ! has_ge<3> ); +static_assert( ! has_ge<4> ); +static_assert( has_ge<5> );