[gcc(refs/users/ppalka/heads/libstdcxx-constrained-algos)] Refactor ranges::{copy, move} and add ranges::{copy_backward, move_backward}

Patrick Palka ppalka@gcc.gnu.org
Thu Jan 23 16:25:00 GMT 2020


https://gcc.gnu.org/g:b9a724ca25bd02973b68befbf874992bf58df0df

commit b9a724ca25bd02973b68befbf874992bf58df0df
Author: Patrick Palka <ppalka@redhat.com>
Date:   Wed Jan 22 22:57:19 2020 -0500

    Refactor ranges::{copy,move} and add ranges::{copy_backward,move_backward}
    
    The unwrapping of normal_iterators, reverse_iterators and move_iterators is now
    "recursively" done in __copy_or_move and likewise in __copy_or_move_backward.
    Unwrapping reverse_iterators is something new that the corresponding STL algos
    don't perform.

Diff:
---
 libstdc++-v3/include/bits/ranges_algo.h            | 249 ++++++++++++++++++---
 .../testsuite/25_algorithms/copy/constrained.cc    |  47 +++-
 .../25_algorithms/copy_backward/constrained.cc     | 193 ++++++++++++++++
 .../testsuite/25_algorithms/move/constrained.cc    |  49 +++-
 .../25_algorithms/move_backward/constrained.cc     | 170 ++++++++++++++
 5 files changed, 670 insertions(+), 38 deletions(-)

diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
index 36eab76..5d6eb14 100644
--- a/libstdc++-v3/include/bits/ranges_algo.h
+++ b/libstdc++-v3/include/bits/ranges_algo.h
@@ -45,6 +45,31 @@ namespace std _GLIBCXX_VISIBILITY(default)
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 namespace ranges
 {
+  namespace __detail
+  {
+    template<typename _Tp>
+    constexpr inline bool __is_normal_iterator = false;
+
+    template<typename _Iterator, typename _Container>
+    constexpr inline bool
+      __is_normal_iterator<__gnu_cxx::__normal_iterator<_Iterator, _Container>>
+      = true;
+
+    template<typename _Tp>
+    constexpr inline bool __is_reverse_iterator = false;
+
+    template<typename _Iterator>
+    constexpr inline bool
+      __is_reverse_iterator<reverse_iterator<_Iterator>> = true;
+
+    template<typename _Tp>
+    constexpr inline bool __is_move_iterator = false;
+
+    template<typename _Iterator>
+    constexpr inline bool
+      __is_move_iterator<move_iterator<_Iterator>> = true;
+  }
+
   template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
 	   typename _Proj = identity,
 	   indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
@@ -816,6 +841,23 @@ namespace ranges
   template<typename _Iter, typename _Out>
   using move_result = copy_result<_Iter, _Out>;
 
+  template<typename _Iter1, typename _Iter2>
+  using move_backward_result = copy_result<_Iter1, _Iter2>;
+
+  template<typename _Iter1, typename _Iter2>
+  using copy_backward_result = copy_result<_Iter1, _Iter2>;
+
+  template<bool _IsMove,
+	   bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   bidirectional_iterator _Out>
+    requires (_IsMove
+	      ? indirectly_movable<_Iter, _Out>
+	      : indirectly_copyable<_Iter, _Out>)
+    constexpr conditional_t<_IsMove,
+			    move_backward_result<_Iter, _Out>,
+			    copy_backward_result<_Iter, _Out>>
+    __copy_or_move_backward(_Iter __first, _Sent __last, _Out __result);
+
   template<bool _IsMove,
 	   input_iterator _Iter, sentinel_for<_Iter> _Sent,
 	   weakly_incrementable _Out>
@@ -829,7 +871,40 @@ namespace ranges
     {
       // TODO: implement more specializations to be at least on par with
       // std::copy/std::move.
-      if constexpr (sized_sentinel_for<_Sent, _Iter>)
+      constexpr bool __normal_iterator_p
+	= (__detail::__is_normal_iterator<_Iter>
+	   || __detail::__is_normal_iterator<_Out>);
+      constexpr bool __reverse_p
+	= (__detail::__is_reverse_iterator<_Iter>
+	   && __detail::__is_reverse_iterator<_Out>);
+      constexpr bool __move_iterator_p = __detail::__is_move_iterator<_Iter>;
+      if constexpr (__move_iterator_p)
+	{
+	  auto [__in, __out]
+	    = ranges::__copy_or_move<true>(std::move(__first).base(),
+					   std::move(__last).base(),
+					   std::move(__result));
+	  return {move_iterator{std::move(__in)}, std::move(__out)};
+	}
+      else if constexpr (__reverse_p)
+	{
+	  auto [__in,__out]
+	    = ranges::__copy_or_move_backward<_IsMove>(__last.base(),
+						       __first.base(),
+						       __result.base());
+	  return {reverse_iterator{std::move(__in)},
+		  reverse_iterator{std::move(__out)}};
+	}
+      else if constexpr (__normal_iterator_p)
+	{
+	  auto [__in,__out]
+	    = ranges::__copy_or_move<_IsMove> (std::__niter_base(__first),
+					       std::__niter_base(__last),
+					       std::__niter_base(__result));
+	  return {std::__niter_wrap(__first, std::move(__in)),
+		  std::__niter_wrap(__result, std::move(__out))};
+	}
+      else if constexpr (sized_sentinel_for<_Sent, _Iter>)
 	{
 	  using _ValueTypeI = iter_value_t<_Iter>;
 	  using _ValueTypeO = iter_value_t<_Out>;
@@ -884,29 +959,9 @@ namespace ranges
     constexpr copy_result<_Iter, _Out>
     copy(_Iter __first, _Sent __last, _Out __result)
     {
-      constexpr bool __move_iterator_p = __is_move_iterator<_Iter>::__value;
-      if constexpr (__move_iterator_p)
-	{
-	  auto __first_base = std::move(__first).base();
-	  auto __last_base = std::move(__last).base();
-	  auto [__in,__out]
-	    = ranges::__copy_or_move<true>(__niter_base(std::move(__first_base)),
-					   __niter_base(std::move(__last_base)),
-					   __niter_base(std::move(__result)));
-	  auto __wrapped_in = std::__niter_wrap(__first_base, std::move(__in));
-	  auto __wrapped_out = std::__niter_wrap(__result, std::move(__out));
-	  return {move_iterator{std::move(__wrapped_in)},
-		  std::move(__wrapped_out)};
-	}
-      else
-	{
-	  auto [__in,__out]
-	    = ranges::__copy_or_move<false>(__niter_base(std::move(__first)),
-					    __niter_base(std::move(__last)),
-					    __niter_base(std::move(__result)));
-	  return {__niter_wrap(std::move(__first), std::move(__in)),
-		  __niter_wrap(std::move(__result), std::move(__out))};
-	}
+      return ranges::__copy_or_move<false>(std::move(__first),
+					   std::move(__last),
+					   std::move(__result));
     }
 
   template<input_range _Range, weakly_incrementable _Out>
@@ -924,17 +979,9 @@ namespace ranges
     constexpr move_result<_Iter, _Out>
     move(_Iter __first, _Sent __last, _Out __result)
     {
-      if constexpr (__is_move_iterator<_Iter>::__value)
-	return ranges::copy(std::move(__first), std::move(__last),
-			    std::move(__result));
-      else
-	{
-	  auto [__in, __out]
-	    = ranges::copy(move_iterator<_Iter>{std::move(__first)},
-			   move_sentinel<_Sent>{std::move(__last)},
-			   std::move(__result));
-	  return {std::move(__in).base(), std::move(__out)};
-	}
+      return ranges::__copy_or_move<true>(std::move(__first),
+					  std::move(__last),
+					  std::move(__result));
     }
 
   template<input_range _Range, weakly_incrementable _Out>
@@ -946,6 +993,138 @@ namespace ranges
 			  std::move(__result));
     }
 
+  template<bool _IsMove,
+	   bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent,
+	   bidirectional_iterator _Out>
+    requires (_IsMove
+	      ? indirectly_movable<_Iter, _Out>
+	      : indirectly_copyable<_Iter, _Out>)
+    constexpr conditional_t<_IsMove,
+			    move_backward_result<_Iter, _Out>,
+			    copy_backward_result<_Iter, _Out>>
+    __copy_or_move_backward(_Iter __first, _Sent __last, _Out __result)
+    {
+      // TODO: implement more specializations to be at least on par with
+      // std::copy_backward/std::move_backward.
+      constexpr bool __normal_iterator_p
+	= (__detail::__is_normal_iterator<_Iter>
+	   || __detail::__is_normal_iterator<_Out>);
+      constexpr bool __reverse_p
+	= (__detail::__is_reverse_iterator<_Iter>
+	   && __detail::__is_reverse_iterator<_Out>);
+      if constexpr (__reverse_p)
+	{
+	  auto [__in,__out]
+	    = ranges::__copy_or_move<_IsMove>(__last.base(),
+					      __first.base(),
+					      __result.base());
+	  return {reverse_iterator{std::move(__in)},
+		  reverse_iterator{std::move(__out)}};
+	}
+      else if constexpr (__normal_iterator_p)
+	{
+	  auto [__in,__out]
+	    = ranges::__copy_or_move_backward<_IsMove>
+	      (std::__niter_base(__first),
+	       std::__niter_base(__last),
+	       std::__niter_base(__result));
+	  return {std::__niter_wrap(__first, std::move(__in)),
+		  std::__niter_wrap(__result, std::move(__out))};
+	}
+      else if constexpr (sized_sentinel_for<_Sent, _Iter>)
+	{
+	  using _ValueTypeI = iter_value_t<_Iter>;
+	  using _ValueTypeO = iter_value_t<_Out>;
+	  constexpr bool __use_memmove
+	    = (is_trivially_copyable_v<_ValueTypeI>
+	       && is_same_v<_ValueTypeI, _ValueTypeO>
+	       && is_pointer_v<_Iter>
+	       && is_pointer_v<_Out>);
+	  if constexpr (__use_memmove)
+	    {
+	      static_assert(_IsMove
+			    ? is_move_assignable_v<_ValueTypeI>
+			    : is_copy_assignable_v<_ValueTypeI>);
+	      auto __num = __last - __first;
+	      if (__num)
+		std::__memmove<_IsMove>(__result - __num, __first, __num);
+	      return {__first + __num, __result - __num};
+	    }
+	  else
+	    {
+	      auto __lasti = ranges::next(__first, __last);
+	      auto __tail = __lasti;
+
+	      for (auto __n = __last - __first; __n > 0; --__n)
+		{
+		  --__tail;
+		  --__result;
+		  if constexpr (_IsMove)
+		    *__result = std::move(*__tail);
+		  else
+		    *__result = *__tail;
+		}
+	      return {std::move(__lasti), std::move(__result)};
+	    }
+	}
+      else
+	{
+	  auto __lasti = ranges::next(__first, __last);
+	  auto __tail = __lasti;
+
+	  while (__first != __tail)
+	    {
+	      --__tail;
+	      --__result;
+	      if constexpr (_IsMove)
+		*__result = std::move(*__tail);
+	      else
+		*__result = *__tail;
+	    }
+	  return {std::move(__lasti), std::move(__result)};
+	}
+    }
+
+  template<bidirectional_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   bidirectional_iterator _Iter2>
+    requires indirectly_copyable<_Iter1, _Iter2>
+    constexpr copy_backward_result<_Iter1, _Iter2>
+    copy_backward(_Iter1 __first, _Sent1 __last, _Iter2 __result)
+    {
+      return ranges::__copy_or_move_backward<false>(std::move(__first),
+						    std::move(__last),
+						    std::move(__result));
+    }
+
+  template<bidirectional_range _Range, bidirectional_iterator _Iter>
+    requires indirectly_copyable<iterator_t<_Range>, _Iter>
+    constexpr copy_backward_result<safe_iterator_t<_Range>, _Iter>
+    copy_backward(_Range&& __r, _Iter __result)
+    {
+      return ranges::copy_backward(ranges::begin(__r), ranges::end(__r),
+				   std::move(__result));
+    }
+
+  template<bidirectional_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	   bidirectional_iterator _Iter2>
+    requires indirectly_movable<_Iter1, _Iter2>
+    constexpr move_backward_result<_Iter1, _Iter2>
+    move_backward(_Iter1 __first, _Sent1 __last, _Iter2 __result)
+    {
+      return ranges::__copy_or_move_backward<true>(std::move(__first),
+						   std::move(__last),
+						   std::move(__result));
+    }
+
+  template<bidirectional_range _Range, bidirectional_iterator _Iter>
+    requires indirectly_movable<iterator_t<_Range>, _Iter>
+    constexpr move_backward_result<safe_iterator_t<_Range>, _Iter>
+    move_backward(_Range&& __r, _Iter __result)
+    {
+      return ranges::move_backward(ranges::begin(__r), ranges::end(__r),
+				   std::move(__result));
+    }
+
   template<typename _Iter1, typename _Iter2>
   using swap_ranges_result = mismatch_result<_Iter1, _Iter2>;
 
diff --git a/libstdc++-v3/testsuite/25_algorithms/copy/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/copy/constrained.cc
index f6cddc5..85f7d64 100644
--- a/libstdc++-v3/testsuite/25_algorithms/copy/constrained.cc
+++ b/libstdc++-v3/testsuite/25_algorithms/copy/constrained.cc
@@ -19,6 +19,7 @@
 // { dg-do run { target c++2a } }
 
 #include <algorithm>
+#include <vector>
 #include <testsuite_hooks.h>
 #include <testsuite_iterators.h>
 
@@ -63,6 +64,51 @@ test01()
       VERIFY( ranges::equal(x, x+3, y, y+3) && in.ptr == x+3 && out.ptr == y+3 );
       VERIFY( ranges::equal(x, z) );
     }
+
+    {
+      std::vector<char> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in, out] = ranges::copy(x, ranges::begin(y));
+      VERIFY( in.base() == x.data()+3 );
+      VERIFY( out.base() == y.data()+3 );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+
+    {
+      std::vector<int> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in, out] = ranges::copy(x, ranges::begin(y));
+      VERIFY( in.base() == x.data()+3 );
+      VERIFY( out.base() == y.data()+3 );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<int> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in,out] = ranges::copy(make_reverse_iterator(x.end()),
+				   make_reverse_iterator(x.begin()),
+				   make_reverse_iterator(y.end()));
+      VERIFY( in.base().base() == x.data()+3 );
+      VERIFY( out.base().base() == y.data() );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<char> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in,out] = ranges::copy(make_reverse_iterator(x.end()),
+				   make_reverse_iterator(x.begin()),
+				   make_reverse_iterator(y.end()));
+      VERIFY( in.base().base() == x.data()+3 );
+      VERIFY( out.base().base() == y.data() );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
 }
 
 struct X
@@ -177,4 +223,3 @@ main()
   test04();
   static_assert(test05());
 }
-
diff --git a/libstdc++-v3/testsuite/25_algorithms/copy_backward/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/copy_backward/constrained.cc
new file mode 100644
index 0000000..900f78a
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/copy_backward/constrained.cc
@@ -0,0 +1,193 @@
+// 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 <algorithm>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+    {
+      int x[7] = { 1, 2, 3, 4, 5, 6, 7 };
+      int y[7] = { 0 };
+      int z[7] = { 1, 2, 3, 4, 5, 6, 7 };
+      auto [in, out] = ranges::copy_backward(x, ranges::end(y));
+      VERIFY( ranges::equal(x, y) && in == x+7 && out == y);
+      VERIFY( ranges::equal(x, z) );
+    }
+
+    {
+      int x[3] = { 1, 2, 3 };
+      char y[4] = { 0 };
+      int z[3] = { 1, 2, 3 };
+      test_container<int, bidirectional_iterator_wrapper> cx(x);
+      test_container<char, bidirectional_iterator_wrapper> cy(y);
+      auto [in, out] = ranges::copy_backward(cx, ranges::end(cy));
+      VERIFY( ranges::equal(x, x+3, y+1, y+4) && in.ptr == x+3 && out.ptr == y+1 );
+      VERIFY( ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<char> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in, out] = ranges::copy_backward(x, ranges::end(y));
+      VERIFY( in.base() == x.data()+3 );
+      VERIFY( out.base() == y.data() );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+
+    {
+      std::vector<int> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in, out] = ranges::copy_backward(x, ranges::end(y));
+      VERIFY( in.base() == x.data()+3 );
+      VERIFY( out.base() == y.data() );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<int> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in,out] = ranges::copy_backward(make_reverse_iterator(x.end()),
+					    make_reverse_iterator(x.begin()),
+					    make_reverse_iterator(y.begin()));
+      VERIFY( in.base().base() == x.data()+3 );
+      VERIFY( out.base().base() == y.data()+3 );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<char> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in,out] = ranges::copy_backward(make_reverse_iterator(x.end()),
+					    make_reverse_iterator(x.begin()),
+					    make_reverse_iterator(y.begin()));
+      VERIFY( in.base().base() == x.data()+3 );
+      VERIFY( out.base().base() == y.data()+3 );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+}
+
+constexpr bool
+test02()
+{
+  bool ok = true;
+  int x[] = { {2}, {2}, {6}, {8}, {10} };
+  int y[] = { {2}, {6}, {8}, {10}, {11}, {2} };
+  const int z[] = { {2}, {2}, {6}, {8}, {10} };
+  auto [in, out] = ranges::copy_backward(x, ranges::end(y));
+  ok &= ranges::equal(x, x+5, y+1, y+6);
+  ok &= (in == x+5);
+  ok &= (out == y+1);
+  ok &= (y[0] == 2);
+  ok &= ranges::equal(x, z);
+  return ok;
+}
+
+/*  move_iterators are always input_iterators and therefore do not model
+ *  bidirectional_iterator, so I think the following tests are rightly invalid.
+
+struct Y
+{
+  int i;
+  int moved = 0;
+
+  constexpr Y(int a) : i(a) { }
+
+  constexpr Y(const Y&) = delete;
+  constexpr Y& operator=(const Y&) = delete;
+
+  constexpr Y(Y&& other)
+  {
+    *this = std::move(other);
+  }
+
+  constexpr Y&
+  operator=(Y&& other)
+  {
+    other.moved++;
+    i = other.i;
+    return *this;
+  }
+
+  friend constexpr bool
+  operator==(const Y& a, const Y& b)
+  { return a.i == b.i; }
+};
+
+void
+test02()
+{
+  Y x[7] = { 1, 2, 3, 4, 5, 6, 7 };
+  Y y[7] = { 0, 0, 0, 0, 0, 0, 0 };
+  Y z[7] = { 1, 2, 3, 4, 5, 6, 7 };
+  test_range<Y, bidirectional_iterator_wrapper> rx(x);
+  auto [in, out] = ranges::copy_backward(std::move_iterator{ranges::begin(rx)},
+					 std::move_sentinel{ranges::end(rx)},
+					 ranges::end(y));
+  VERIFY( ranges::equal(x, y) && std::move(in).base().ptr == x+7 && out == y );
+  VERIFY( ranges::equal(x, z) );
+  for (const auto& v : x)
+    VERIFY( v.moved == 1 );
+  for (const auto& v : y)
+    VERIFY( v.moved == 0 );
+}
+
+constexpr bool
+test03()
+{
+  bool ok = true;
+  Y x[7] = { 1, 2, 3, 4, 5, 6, 7 };
+  Y y[7] = { 0, 0, 0, 0, 0, 0, 0 };
+  Y z[7] = { 1, 2, 3, 4, 5, 6, 7 };
+  auto [in, out] = ranges::copy_backward(std::move_iterator{ranges::begin(x)},
+					 std::move_sentinel{ranges::end(x)},
+					 ranges::end(y));
+  ok &= ranges::equal(x, y);
+  ok &= in.base() == x+7;
+  ok &= out == y;
+  ok &= ranges::equal(x, z);
+  for (const auto& v : x)
+    ok &= v.moved == 1;
+  for (const auto& v : y)
+    ok &= v.moved == 0;
+  return ok;
+}
+*/
+
+int
+main()
+{
+  test01();
+  static_assert(test02());
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/move/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/move/constrained.cc
index ab4d283..d205b35 100644
--- a/libstdc++-v3/testsuite/25_algorithms/move/constrained.cc
+++ b/libstdc++-v3/testsuite/25_algorithms/move/constrained.cc
@@ -19,6 +19,7 @@
 // { dg-do run { target c++2a } }
 
 #include <algorithm>
+#include <vector>
 #include <testsuite_hooks.h>
 #include <testsuite_iterators.h>
 
@@ -35,6 +36,7 @@ struct X
   int i;
   int moved = 0;
 
+  constexpr X() : i(0) { }
   constexpr X(int a) : i(a) { }
 
   constexpr X(const X&) = delete;
@@ -91,6 +93,51 @@ test01()
       VERIFY( ranges::equal(x, x+3, y, y+3) && in.ptr == x+3 && out.ptr == y+3 );
       VERIFY( ranges::equal(x, z) );
     }
+
+    {
+      std::vector<char> x= {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in, out] = ranges::move(x, ranges::begin(y));
+      VERIFY( in.base() == x.data()+3 );
+      VERIFY( out.base() == y.data()+3 );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+
+    {
+      std::vector<int> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in, out] = ranges::move(x, ranges::begin(y));
+      VERIFY( in.base() == x.data()+3 );
+      VERIFY( out.base() == y.data()+3 );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<int> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in,out] = ranges::move(make_reverse_iterator(x.end()),
+				   make_reverse_iterator(x.begin()),
+				   make_reverse_iterator(y.end()));
+      VERIFY( in.base().base() == x.data()+3 );
+      VERIFY( out.base().base() == y.data() );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<char> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in,out] = ranges::move(make_reverse_iterator(x.end()),
+				   make_reverse_iterator(x.begin()),
+				   make_reverse_iterator(y.end()));
+      VERIFY( in.base().base() == x.data()+3 );
+      VERIFY( out.base().base() == y.data() );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
 }
 
 void
@@ -154,5 +201,3 @@ main()
   static_assert(test03());
   test04();
 }
-
-
diff --git a/libstdc++-v3/testsuite/25_algorithms/move_backward/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/move_backward/constrained.cc
new file mode 100644
index 0000000..3c4aa5d
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/move_backward/constrained.cc
@@ -0,0 +1,170 @@
+// 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 <algorithm>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_container;
+using __gnu_test::test_range;
+using __gnu_test::bidirectional_iterator_wrapper;
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  int i;
+  int moved = 0;
+
+  constexpr X() : i(0) { }
+  constexpr X(int a) : i(a) { }
+
+  constexpr X(const X&) = delete;
+  constexpr X& operator=(const X&) = delete;
+
+  constexpr X(X&& other)
+  {
+    *this = std::move(other);
+  }
+
+  constexpr X&
+  operator=(X&& other)
+  {
+    other.moved++;
+    i = other.i;
+    return *this;
+  }
+
+  friend constexpr bool
+  operator==(const X& a, const X& b)
+  { return a.i == b.i; }
+};
+
+void
+test01()
+{
+    {
+      X x[7] = { 1, 2, 3, 4, 5, 6, 7 };
+      X y[7] = { 0, 0, 0, 0, 0, 0, 0 };
+      X z[7] = { 1, 2, 3, 4, 5, 6, 7 };
+      auto [in, out] = ranges::move_backward(x, y+7);
+      VERIFY( ranges::equal(x, y) && in == x+7 && out == y );
+      VERIFY( ranges::equal(x, z) );
+    }
+
+    {
+      int x[3] = { 1, 2, 3 };
+      char y[4] = { 0 };
+      int z[3] = { 1, 2, 3 };
+      test_container<int, bidirectional_iterator_wrapper> cx(x);
+      test_container<char, bidirectional_iterator_wrapper> cy(y);
+      auto [in, out] = ranges::move_backward(cx, cy.end());
+      VERIFY( ranges::equal(x, x+3, y+1, y+4) && in.ptr == x+3 && out.ptr == y+1 );
+      VERIFY( ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<char> x= {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in, out] = ranges::move_backward(x, ranges::end(y));
+      VERIFY( in.base() == x.data()+3 );
+      VERIFY( out.base() == y.data() );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+
+    {
+      std::vector<int> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in, out] = ranges::move_backward(x, ranges::end(y));
+      VERIFY( in.base() == x.data()+3 );
+      VERIFY( out.base() == y.data() );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<int> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in,out] = ranges::move_backward(make_reverse_iterator(x.end()),
+					    make_reverse_iterator(x.begin()),
+					    make_reverse_iterator(y.begin()));
+      VERIFY( in.base().base() == x.data()+3 );
+      VERIFY( out.base().base() == y.data()+3 );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+
+    {
+      std::vector<char> x = {1,2,3};
+      std::vector<int> y(3);
+      const int z[3] = { 1, 2, 3 };
+      auto [in,out] = ranges::move_backward(make_reverse_iterator(x.end()),
+					    make_reverse_iterator(x.begin()),
+					    make_reverse_iterator(y.begin()));
+      VERIFY( in.base().base() == x.data()+3 );
+      VERIFY( out.base().base() == y.data()+3 );
+      VERIFY( ranges::equal(y, z) && ranges::equal(x, z) );
+    }
+}
+
+void
+test02()
+{
+  X x[] = { {2}, {2}, {6}, {8}, {10} };
+  X y[] = { {2}, {6}, {8}, {10}, {11}, {2} };
+  const X z[] = { {2}, {2}, {6}, {8}, {10} };
+  auto [in, out] = ranges::move_backward(x, ranges::end(y));
+  VERIFY( ranges::equal(x, x+5, y+1, y+6) );
+  VERIFY( in == x+5 );
+  VERIFY( out == y+1 );
+  VERIFY( y[0].i == 2 );
+  VERIFY( ranges::equal(x, z) );
+  VERIFY( ranges::count(x, 1, &X::moved) == 5 );
+  VERIFY( ranges::count(y, 0, &X::moved) == 6 );
+}
+
+constexpr bool
+test03()
+{
+  bool ok = true;
+  X x[] = { {2}, {2}, {6}, {8}, {10} };
+  X y[] = { {2}, {6}, {8}, {10}, {11}, {2} };
+  const X z[] = { {2}, {2}, {6}, {8}, {10} };
+  auto [in, out] = ranges::move_backward(x, ranges::end(y));
+  ok &= ranges::equal(x, x+5, y+1, y+6);
+  ok &= (in == x+5);
+  ok &= (out == y+1);
+  ok &= (y[0].i == 2);
+  ok &= ranges::equal(x, z);
+  ok &= ranges::count(x, 1, &X::moved) == 5;
+  ok &= ranges::count(y, 0, &X::moved) == 6;
+  return ok;
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  static_assert(test03());
+}



More information about the Libstdc++-cvs mailing list