[committed,2/3] libstdc++: Add remaining C++20 changes to iterator adaptors

Message ID 20200327232944.GC71320@redhat.com
State New
Headers show
Series
  • [committed,1/3] libstdc++: Implement C++20 changes to insert iterators
Related show

Commit Message

Xionghu Luo via Gcc-patches March 27, 2020, 11:29 p.m.
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.

Comments

Xionghu Luo via Gcc-patches March 28, 2020, 12:16 a.m. | #1
On 27/03/20 23:29 +0000, Jonathan Wakely wrote:
>@@ -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(); }


Drat, these relational operators use the wrong operator. The sense
needs to be flipped because they're reverse_iterators.

Looks like I lost an edit to this file that I made on another machine,
and committed the wrong version. I'll fix it ASAP.

>+  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
Xionghu Luo via Gcc-patches March 28, 2020, 10:22 p.m. | #2
On 28/03/20 00:16 +0000, Jonathan Wakely wrote:
>On 27/03/20 23:29 +0000, Jonathan Wakely wrote:

>>@@ -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(); }

>

>Drat, these relational operators use the wrong operator. The sense

>needs to be flipped because they're reverse_iterators.

>

>Looks like I lost an edit to this file that I made on another machine,

>and committed the wrong version. I'll fix it ASAP.


Here's the fix. Tested powerpc64le-linux, committed to master.
commit 42cda3ba45fca30e73e1c35d8e19b5ec8af24d98
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Sat Mar 28 21:52:13 2020 +0000

    libstdc++: Fix std::reverse_iterator relational operators
    
    My recent changes to reverse_iterator's comparisons was not the version
    of the code (or tests) that I meant to commit, and broke the relational
    operators. This fixes them to reverse the order of the comparisons on
    the base() iterators.
    
    This also replaces the SFINAE constraints in the return type of the
    reverse_iterator and move_iterator comparisons with a requires-clause.
    This ensures the constrained overloads are preferred to unconstrained
    ones. This means the non-standard same-type overloads can be omitted for
    C++20 because they're not needed to solve the problem with std::rel_ops
    or the testsuite's greedy_ops::X type.
    
            * include/bits/stl_iterator.h (reverse_iterator): Use requires-clause
            to constrain C++20 versions of comparison operators. Fix backwards
            logic of relational operators.
            (move_iterator): Use requires-clause to constrain comparison operators
            in C++20. Do not declare non-standard same-type overloads for C++20.
            * testsuite/24_iterators/move_iterator/rel_ops_c++20.cc: Check result
            of comparisons and check using greedy_ops type.
            * testsuite/24_iterators/reverse_iterator/rel_ops_c++20.cc: Likewise.
            * testsuite/24_iterators/move_iterator/greedy_ops.cc: Remove redundant
            main function from compile-only test.
            * testsuite/24_iterators/reverse_iterator/greedy_ops.cc: Likewise.

diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h
index e68f66a2b89..d7972b71998 100644
--- a/libstdc++-v3/include/bits/stl_iterator.h
+++ b/libstdc++-v3/include/bits/stl_iterator.h
@@ -341,9 +341,6 @@ _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.
@@ -354,7 +351,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    *  iterators.
    *
   */
-#if __cplusplus <= 201703L
+#if __cplusplus <= 201703L || ! defined __cpp_lib_concepts
   template<typename _Iterator>
     inline _GLIBCXX17_CONSTEXPR bool
     operator==(const reverse_iterator<_Iterator>& __x,
@@ -430,46 +427,53 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return !(__x < __y); }
 #else // C++20
   template<typename _IteratorL, typename _IteratorR>
-    constexpr auto
+    constexpr bool
     operator==(const reverse_iterator<_IteratorL>& __x,
 	       const reverse_iterator<_IteratorR>& __y)
-    -> decltype(__detail::__convbool(__x.base() == __y.base()))
+    requires requires { { __x.base() == __y.base() } -> convertible_to<bool>; }
     { return __x.base() == __y.base(); }
 
   template<typename _IteratorL, typename _IteratorR>
-    constexpr auto
+    constexpr bool
     operator!=(const reverse_iterator<_IteratorL>& __x,
 	       const reverse_iterator<_IteratorR>& __y)
-    -> decltype(__detail::__convbool(__x.base() != __y.base()))
+    requires requires { { __x.base() != __y.base() } -> convertible_to<bool>; }
     { return __x.base() != __y.base(); }
 
   template<typename _IteratorL, typename _IteratorR>
-    constexpr auto
+    constexpr bool
     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()))
+    requires requires { { __x.base() > __y.base() } -> convertible_to<bool>; }
     { 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(); }
+    constexpr bool
+    operator>(const reverse_iterator<_IteratorL>& __x,
+	      const reverse_iterator<_IteratorR>& __y)
+    requires requires { { __x.base() < __y.base() } -> convertible_to<bool>; }
+    { return __x.base() < __y.base(); }
 
   template<typename _IteratorL, typename _IteratorR>
-    constexpr auto
+    constexpr bool
+    operator<=(const reverse_iterator<_IteratorL>& __x,
+	       const reverse_iterator<_IteratorR>& __y)
+    requires requires { { __x.base() >= __y.base() } -> convertible_to<bool>; }
+    { return __x.base() >= __y.base(); }
+
+  template<typename _IteratorL, typename _IteratorR>
+    constexpr bool
     operator>=(const reverse_iterator<_IteratorL>& __x,
 	       const reverse_iterator<_IteratorR>& __y)
-    -> decltype(__detail::__convbool(__x.base() >= __y.base()))
-    { return __x.base() >= __y.base(); }
+    requires requires { { __x.base() <= __y.base() } -> convertible_to<bool>; }
+    { return __x.base() <= __y.base(); }
+
+  template<typename _IteratorL,
+	   three_way_comparable_with<_IteratorL> _IteratorR>
+    constexpr compare_three_way_result_t<_IteratorL, _IteratorR>
+    operator<=>(const reverse_iterator<_IteratorL>& __x,
+		const reverse_iterator<_IteratorR>& __y)
+    { return __y.base() <=> __x.base(); }
 #endif // C++20
   //@}
 
@@ -1413,24 +1417,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif // C++20
     };
 
-  // Note: See __normal_iterator operators note from Gaby to understand
-  // why there are always 2 versions for most of the move_iterator
-  // operators.
   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()); }
+    requires requires { { __x.base() == __y.base() } -> convertible_to<bool>; }
 #endif
     { return __x.base() == __y.base(); }
 
-  template<typename _Iterator>
-    inline _GLIBCXX17_CONSTEXPR bool
-    operator==(const move_iterator<_Iterator>& __x,
-	       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>
@@ -1444,12 +1439,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     operator!=(const move_iterator<_IteratorL>& __x,
 	       const move_iterator<_IteratorR>& __y)
     { return !(__x == __y); }
-
-  template<typename _Iterator>
-    inline _GLIBCXX17_CONSTEXPR bool
-    operator!=(const move_iterator<_Iterator>& __x,
-	       const move_iterator<_Iterator>& __y)
-    { return !(__x == __y); }
 #endif
 
   template<typename _IteratorL, typename _IteratorR>
@@ -1457,60 +1446,81 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     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()); }
+    requires requires { { __x.base() < __y.base() } -> convertible_to<bool>; }
 #endif
     { return __x.base() < __y.base(); }
 
-  template<typename _Iterator>
-    inline _GLIBCXX17_CONSTEXPR bool
-    operator<(const move_iterator<_Iterator>& __x,
-	      const move_iterator<_Iterator>& __y)
-    { return __x.base() < __y.base(); }
-
   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(__y.base() < __x.base()); }
+    requires requires { { __y.base() < __x.base() } -> convertible_to<bool>; }
 #endif
     { return !(__y < __x); }
 
-  template<typename _Iterator>
-    inline _GLIBCXX17_CONSTEXPR bool
-    operator<=(const move_iterator<_Iterator>& __x,
-	       const move_iterator<_Iterator>& __y)
-    { return !(__y < __x); }
-
   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(__y.base() < __x.base()); }
+    requires requires { { __y.base() < __x.base() } -> convertible_to<bool>; }
 #endif
     { return __y < __x; }
 
-  template<typename _Iterator>
-    inline _GLIBCXX17_CONSTEXPR bool
-    operator>(const move_iterator<_Iterator>& __x,
-	      const move_iterator<_Iterator>& __y)
-    { return __y < __x; }
-
   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()); }
+    requires requires { { __x.base() < __y.base() } -> convertible_to<bool>; }
 #endif
     { return !(__x < __y); }
 
+#if ! (__cplusplus > 201703L && __cpp_lib_concepts)
+  // Note: See __normal_iterator operators note from Gaby to understand
+  // why we have these extra overloads for some move_iterator operators.
+
+  // These extra overloads are not needed in C++20, because the ones above
+  // are constrained with a requires-clause and so overload resolution will
+  // prefer them to greedy unconstrained function templates.
+
+  template<typename _Iterator>
+    inline _GLIBCXX17_CONSTEXPR bool
+    operator==(const move_iterator<_Iterator>& __x,
+	       const move_iterator<_Iterator>& __y)
+    { return __x.base() == __y.base(); }
+
+  template<typename _Iterator>
+    inline _GLIBCXX17_CONSTEXPR bool
+    operator!=(const move_iterator<_Iterator>& __x,
+	       const move_iterator<_Iterator>& __y)
+    { return !(__x == __y); }
+
+  template<typename _Iterator>
+    inline _GLIBCXX17_CONSTEXPR bool
+    operator<(const move_iterator<_Iterator>& __x,
+	      const move_iterator<_Iterator>& __y)
+    { return __x.base() < __y.base(); }
+
+  template<typename _Iterator>
+    inline _GLIBCXX17_CONSTEXPR bool
+    operator<=(const move_iterator<_Iterator>& __x,
+	       const move_iterator<_Iterator>& __y)
+    { return !(__y < __x); }
+
+  template<typename _Iterator>
+    inline _GLIBCXX17_CONSTEXPR bool
+    operator>(const move_iterator<_Iterator>& __x,
+	      const move_iterator<_Iterator>& __y)
+    { return __y < __x; }
+
   template<typename _Iterator>
     inline _GLIBCXX17_CONSTEXPR bool
     operator>=(const move_iterator<_Iterator>& __x,
 	       const move_iterator<_Iterator>& __y)
     { return !(__x < __y); }
+#endif // ! C++20
 
   // DR 685.
   template<typename _IteratorL, typename _IteratorR>
diff --git a/libstdc++-v3/testsuite/24_iterators/move_iterator/greedy_ops.cc b/libstdc++-v3/testsuite/24_iterators/move_iterator/greedy_ops.cc
index 66da0a4cff9..0e6d598cd43 100644
--- a/libstdc++-v3/testsuite/24_iterators/move_iterator/greedy_ops.cc
+++ b/libstdc++-v3/testsuite/24_iterators/move_iterator/greedy_ops.cc
@@ -24,7 +24,7 @@ void test01()
   typedef std::move_iterator<greedy_ops::X*> iterator_type;
 
   iterator_type it(nullptr);
-  
+
   it == it;
   it != it;
   it < it;
@@ -35,9 +35,3 @@ void test01()
   1 + it;
   it + 1;
 }
-
-int main() 
-{ 
-  test01();
-  return 0;
-}
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
index 4e7b9d01e15..a530923f8ad 100644
--- 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
@@ -127,3 +127,37 @@ static_assert( has_le<2> );
 static_assert( ! has_ge<0> );
 static_assert( has_ge<1> );
 static_assert( ! has_ge<2> );
+
+int arr[3] = { 1, 2, 3 };
+constexpr std::move_iterator<int*> beg{std::begin(arr)};
+constexpr std::move_iterator<const int*> cbeg{std::cbegin(arr)};
+static_assert( beg == cbeg );
+static_assert( beg <= cbeg );
+static_assert( beg >= cbeg );
+static_assert( std::is_eq(beg <=> cbeg) );
+constexpr std::move_iterator<const int*> cend{std::cend(arr)};
+static_assert( beg != cend );
+static_assert( beg < cend );
+static_assert( cend > beg );
+static_assert( beg <= cend );
+static_assert( cend >= beg );
+static_assert( std::is_lt(beg <=> cend) );
+
+#include <testsuite_greedy_ops.h>
+
+void test01()
+{
+  typedef std::move_iterator<greedy_ops::X*> iterator_type;
+
+  iterator_type it(nullptr);
+
+  it == it;
+  it != it;
+  it < it;
+  it <= it;
+  it > it;
+  it >= it;
+  // it - it;  // See PR libstdc++/71771
+  1 + it;
+  it + 1;
+}
diff --git a/libstdc++-v3/testsuite/24_iterators/reverse_iterator/greedy_ops.cc b/libstdc++-v3/testsuite/24_iterators/reverse_iterator/greedy_ops.cc
index 60d4611e690..a5ea4524604 100644
--- a/libstdc++-v3/testsuite/24_iterators/reverse_iterator/greedy_ops.cc
+++ b/libstdc++-v3/testsuite/24_iterators/reverse_iterator/greedy_ops.cc
@@ -24,7 +24,7 @@ void test01()
   typedef std::reverse_iterator<greedy_ops::X*> iterator_type;
 
   iterator_type it;
-  
+
   it == it;
   it != it;
   it < it;
@@ -37,9 +37,3 @@ void test01()
   1 + it;
   it + 1;
 }
-
-int main() 
-{ 
-  test01();
-  return 0;
-}
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
index 3e91a0396fe..a382ae52483 100644
--- 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
@@ -78,10 +78,10 @@ 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;
+bool b2 = l2 > r;
+bool b3 = l3 < r;
+bool b4 = l4 >= r;
+bool b5 = l5 <= r;
 
 template<int N>
   concept has_eq
@@ -129,15 +129,15 @@ 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<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<2> );
+static_assert( ! has_gt<3> );
 static_assert( ! has_gt<4> );
 static_assert( ! has_gt<5> );
 
@@ -145,12 +145,49 @@ 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_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> );
+static_assert( has_ge<4> );
+static_assert( ! has_ge<5> );
+
+int arr[3] = { 1, 2, 3 };
+constexpr std::reverse_iterator<int*> rbeg = std::rbegin(arr);
+constexpr std::reverse_iterator<const int*> crbeg = std::crbegin(arr);
+static_assert( rbeg == crbeg );
+static_assert( rbeg <= crbeg );
+static_assert( rbeg >= crbeg );
+static_assert( std::is_eq(rbeg <=> crbeg) );
+constexpr std::reverse_iterator<const int*> crend = std::crend(arr);
+static_assert( rbeg != crend );
+static_assert( rbeg < crend );
+static_assert( crend > rbeg );
+static_assert( rbeg <= crend );
+static_assert( crend >= rbeg );
+static_assert( std::is_lt(rbeg <=> crend) );
+
+#include <testsuite_greedy_ops.h>
+
+// copied from 24_iterators/reverse_iterator/greedy_ops.cc
+void test01()
+{
+  typedef std::reverse_iterator<greedy_ops::X*> iterator_type;
+
+  iterator_type it;
+
+  it == it;
+  it != it;
+  it < it;
+  it <= it;
+  it > it;
+  it >= it;
+#if __cplusplus < 201103L
+  it - it; // See PR libstdc++/71771
+#endif
+  1 + it;
+  it + 1;
+}

Patch

commit 81a8d137c22953df2ea046466c62cd26c0dba103
Author: Jonathan Wakely <jwakely@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> );