[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