forked from OSchip/llvm-project
[libc++] Implement ranges::move{, _backward}
This patch also adds a new optimization to `std::move`. It unwraps three `reverse_iterator`s if the wrapped iterator is a `contiguous_iterator` and the iterated type is trivially_movable. This allows us to simplify `ranges::move_backward` to a forward to `std::move` without any pessimization. Reviewed By: var-const, #libc Spies: libcxx-commits, mgorny Differential Revision: https://reviews.llvm.org/D126616
This commit is contained in:
parent
ea38744372
commit
2c3bbac0c7
|
@ -99,6 +99,8 @@ set(files
|
|||
__algorithm/ranges_minmax.h
|
||||
__algorithm/ranges_minmax_element.h
|
||||
__algorithm/ranges_mismatch.h
|
||||
__algorithm/ranges_move.h
|
||||
__algorithm/ranges_move_backward.h
|
||||
__algorithm/ranges_none_of.h
|
||||
__algorithm/ranges_replace.h
|
||||
__algorithm/ranges_replace_if.h
|
||||
|
|
|
@ -11,7 +11,10 @@
|
|||
|
||||
#include <__algorithm/unwrap_iter.h>
|
||||
#include <__config>
|
||||
#include <__iterator/iterator_traits.h>
|
||||
#include <__iterator/reverse_iterator.h>
|
||||
#include <__utility/move.h>
|
||||
#include <__utility/pair.h>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
|
@ -23,53 +26,88 @@ _LIBCPP_BEGIN_NAMESPACE_STD
|
|||
|
||||
// move
|
||||
|
||||
template <class _InputIterator, class _OutputIterator>
|
||||
template <class _InIter, class _Sent, class _OutIter>
|
||||
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
|
||||
_OutputIterator
|
||||
__move_constexpr(_InputIterator __first, _InputIterator __last, _OutputIterator __result)
|
||||
{
|
||||
for (; __first != __last; ++__first, (void) ++__result)
|
||||
*__result = _VSTD::move(*__first);
|
||||
return __result;
|
||||
pair<_InIter, _OutIter> __move_impl(_InIter __first, _Sent __last, _OutIter __result) {
|
||||
while (__first != __last) {
|
||||
*__result = std::move(*__first);
|
||||
++__first;
|
||||
++__result;
|
||||
}
|
||||
return std::make_pair(std::move(__first), std::move(__result));
|
||||
}
|
||||
|
||||
template <class _InType,
|
||||
class _OutType,
|
||||
class = __enable_if_t<is_same<typename remove_const<_InType>::type, _OutType>::value
|
||||
&& is_trivially_move_assignable<_OutType>::value> >
|
||||
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
pair<_InType*, _OutType*> __move_impl(_InType* __first, _InType* __last, _OutType* __result) {
|
||||
if (__libcpp_is_constant_evaluated()
|
||||
// TODO: Remove this once GCC supports __builtin_memmove during constant evaluation
|
||||
#ifndef _LIBCPP_COMPILER_GCC
|
||||
&& !is_trivially_copyable<_InType>::value
|
||||
#endif
|
||||
)
|
||||
return std::__move_impl<_InType*, _InType*, _OutType*>(__first, __last, __result);
|
||||
const size_t __n = static_cast<size_t>(__last - __first);
|
||||
::__builtin_memmove(__result, __first, __n * sizeof(_OutType));
|
||||
return std::make_pair(__first + __n, __result + __n);
|
||||
}
|
||||
|
||||
template <class>
|
||||
struct __is_trivially_move_assignable_unwrapped_impl : false_type {};
|
||||
|
||||
template <class _Type>
|
||||
struct __is_trivially_move_assignable_unwrapped_impl<_Type*> : is_trivially_move_assignable<_Type> {};
|
||||
|
||||
template <class _Iter>
|
||||
struct __is_trivially_move_assignable_unwrapped
|
||||
: __is_trivially_move_assignable_unwrapped_impl<decltype(std::__unwrap_iter<_Iter>(std::declval<_Iter>()))> {};
|
||||
|
||||
template <class _InIter,
|
||||
class _OutIter,
|
||||
__enable_if_t<is_same<typename remove_const<typename iterator_traits<_InIter>::value_type>::type,
|
||||
typename iterator_traits<_OutIter>::value_type>::value
|
||||
&& __is_cpp17_contiguous_iterator<_InIter>::value
|
||||
&& __is_cpp17_contiguous_iterator<_OutIter>::value
|
||||
&& is_trivially_move_assignable<__iter_value_type<_OutIter> >::value, int> = 0>
|
||||
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
|
||||
pair<reverse_iterator<_InIter>, reverse_iterator<_OutIter> >
|
||||
__move_impl(reverse_iterator<_InIter> __first,
|
||||
reverse_iterator<_InIter> __last,
|
||||
reverse_iterator<_OutIter> __result) {
|
||||
auto __first_base = std::__unwrap_iter(__first.base());
|
||||
auto __last_base = std::__unwrap_iter(__last.base());
|
||||
auto __result_base = std::__unwrap_iter(__result.base());
|
||||
auto __result_first = __result_base - (__first_base - __last_base);
|
||||
std::__move_impl(__last_base, __first_base, __result_first);
|
||||
return std::make_pair(__last, reverse_iterator<_OutIter>(std::__rewrap_iter(__result.base(), __result_first)));
|
||||
}
|
||||
|
||||
template <class _InIter, class _Sent, class _OutIter>
|
||||
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
__enable_if_t<is_copy_constructible<_InIter>::value
|
||||
&& is_copy_constructible<_Sent>::value
|
||||
&& is_copy_constructible<_OutIter>::value, pair<_InIter, _OutIter> >
|
||||
__move(_InIter __first, _Sent __last, _OutIter __result) {
|
||||
auto __ret = std::__move_impl(std::__unwrap_iter(__first), std::__unwrap_iter(__last), std::__unwrap_iter(__result));
|
||||
return std::make_pair(std::__rewrap_iter(__first, __ret.first), std::__rewrap_iter(__result, __ret.second));
|
||||
}
|
||||
|
||||
template <class _InIter, class _Sent, class _OutIter>
|
||||
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
__enable_if_t<!is_copy_constructible<_InIter>::value
|
||||
|| !is_copy_constructible<_Sent>::value
|
||||
|| !is_copy_constructible<_OutIter>::value, pair<_InIter, _OutIter> >
|
||||
__move(_InIter __first, _Sent __last, _OutIter __result) {
|
||||
return std::__move_impl(std::move(__first), std::move(__last), std::move(__result));
|
||||
}
|
||||
|
||||
template <class _InputIterator, class _OutputIterator>
|
||||
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
|
||||
_OutputIterator
|
||||
__move(_InputIterator __first, _InputIterator __last, _OutputIterator __result)
|
||||
{
|
||||
return _VSTD::__move_constexpr(__first, __last, __result);
|
||||
}
|
||||
|
||||
template <class _Tp, class _Up>
|
||||
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
|
||||
typename enable_if
|
||||
<
|
||||
is_same<typename remove_const<_Tp>::type, _Up>::value &&
|
||||
is_trivially_move_assignable<_Up>::value,
|
||||
_Up*
|
||||
>::type
|
||||
__move(_Tp* __first, _Tp* __last, _Up* __result)
|
||||
{
|
||||
const size_t __n = static_cast<size_t>(__last - __first);
|
||||
if (__n > 0)
|
||||
_VSTD::memmove(__result, __first, __n * sizeof(_Up));
|
||||
return __result + __n;
|
||||
}
|
||||
|
||||
template <class _InputIterator, class _OutputIterator>
|
||||
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
|
||||
_OutputIterator
|
||||
move(_InputIterator __first, _InputIterator __last, _OutputIterator __result)
|
||||
{
|
||||
if (__libcpp_is_constant_evaluated()) {
|
||||
return _VSTD::__move_constexpr(__first, __last, __result);
|
||||
} else {
|
||||
return _VSTD::__rewrap_iter(__result,
|
||||
_VSTD::__move(_VSTD::__unwrap_iter(__first),
|
||||
_VSTD::__unwrap_iter(__last),
|
||||
_VSTD::__unwrap_iter(__result)));
|
||||
}
|
||||
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
|
||||
_OutputIterator move(_InputIterator __first, _InputIterator __last, _OutputIterator __result) {
|
||||
return std::__move(__first, __last, __result).second;
|
||||
}
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP___ALGORITHM_RANGES_MOVE_H
|
||||
#define _LIBCPP___ALGORITHM_RANGES_MOVE_H
|
||||
|
||||
#include <__algorithm/in_out_result.h>
|
||||
#include <__algorithm/move.h>
|
||||
#include <__config>
|
||||
#include <__iterator/concepts.h>
|
||||
#include <__iterator/iter_move.h>
|
||||
#include <__ranges/access.h>
|
||||
#include <__ranges/concepts.h>
|
||||
#include <__ranges/dangling.h>
|
||||
#include <__utility/move.h>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
namespace ranges {
|
||||
|
||||
template <class _InIter, class _OutIter>
|
||||
using move_result = in_out_result<_InIter, _OutIter>;
|
||||
|
||||
namespace __move {
|
||||
struct __fn {
|
||||
|
||||
template <class _InIter, class _Sent, class _OutIter>
|
||||
requires __iter_move::__move_deref<_InIter> // check that we are allowed to std::move() the value
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr static
|
||||
move_result<_InIter, _OutIter> __move_impl(_InIter __first, _Sent __last, _OutIter __result) {
|
||||
auto __ret = std::__move(std::move(__first), std::move(__last), std::move(__result));
|
||||
return {std::move(__ret.first), std::move(__ret.second)};
|
||||
}
|
||||
|
||||
template <class _InIter, class _Sent, class _OutIter>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr static
|
||||
move_result<_InIter, _OutIter> __move_impl(_InIter __first, _Sent __last, _OutIter __result) {
|
||||
while (__first != __last) {
|
||||
*__result = ranges::iter_move(__first);
|
||||
++__first;
|
||||
++__result;
|
||||
}
|
||||
return {std::move(__first), std::move(__result)};
|
||||
}
|
||||
|
||||
template <input_iterator _InIter, sentinel_for<_InIter> _Sent, weakly_incrementable _OutIter>
|
||||
requires indirectly_movable<_InIter, _OutIter>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr
|
||||
move_result<_InIter, _OutIter> operator()(_InIter __first, _Sent __last, _OutIter __result) const {
|
||||
return __move_impl(std::move(__first), std::move(__last), std::move(__result));
|
||||
}
|
||||
|
||||
template <input_range _Range, weakly_incrementable _OutIter>
|
||||
requires indirectly_movable<iterator_t<_Range>, _OutIter>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr
|
||||
move_result<borrowed_iterator_t<_Range>, _OutIter> operator()(_Range&& __range, _OutIter __result) const {
|
||||
return __move_impl(ranges::begin(__range), ranges::end(__range), std::move(__result));
|
||||
}
|
||||
|
||||
};
|
||||
} // namespace __move
|
||||
|
||||
inline namespace __cpo {
|
||||
inline constexpr auto move = __move::__fn{};
|
||||
} // namespace __cpo
|
||||
} // namespace ranges
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
|
||||
|
||||
#endif // _LIBCPP___ALGORITHM_RANGES_MOVE_H
|
|
@ -0,0 +1,75 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP___ALGORITHM_RANGES_MOVE_BACKWARD_H
|
||||
#define _LIBCPP___ALGORITHM_RANGES_MOVE_BACKWARD_H
|
||||
|
||||
#include <__algorithm/in_out_result.h>
|
||||
#include <__algorithm/ranges_move.h>
|
||||
#include <__config>
|
||||
#include <__iterator/concepts.h>
|
||||
#include <__iterator/iter_move.h>
|
||||
#include <__iterator/next.h>
|
||||
#include <__iterator/reverse_iterator.h>
|
||||
#include <__ranges/access.h>
|
||||
#include <__ranges/concepts.h>
|
||||
#include <__ranges/dangling.h>
|
||||
#include <__utility/move.h>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
namespace ranges {
|
||||
|
||||
template <class _InIter, class _OutIter>
|
||||
using move_backward_result = in_out_result<_InIter, _OutIter>;
|
||||
|
||||
namespace __move_backward {
|
||||
struct __fn {
|
||||
|
||||
template <class _InIter, class _Sent, class _OutIter>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr static
|
||||
move_backward_result<_InIter, _OutIter> __move_backward_impl(_InIter __first, _Sent __last, _OutIter __result) {
|
||||
auto __ret = ranges::move(std::make_reverse_iterator(ranges::next(__first, __last)),
|
||||
std::make_reverse_iterator(__first),
|
||||
std::make_reverse_iterator(__result));
|
||||
return {std::move(__ret.in.base()), std::move(__ret.out.base())};
|
||||
}
|
||||
|
||||
template <bidirectional_iterator _InIter, sentinel_for<_InIter> _Sent, bidirectional_iterator _OutIter>
|
||||
requires indirectly_movable<_InIter, _OutIter>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr
|
||||
move_backward_result<_InIter, _OutIter> operator()(_InIter __first, _Sent __last, _OutIter __result) const {
|
||||
return __move_backward_impl(std::move(__first), std::move(__last), std::move(__result));
|
||||
}
|
||||
|
||||
template <bidirectional_range _Range, bidirectional_iterator _Iter>
|
||||
requires indirectly_movable<iterator_t<_Range>, _Iter>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr
|
||||
move_backward_result<borrowed_iterator_t<_Range>, _Iter> operator()(_Range&& __range, _Iter __result) const {
|
||||
return __move_backward_impl(ranges::begin(__range), ranges::end(__range), std::move(__result));
|
||||
}
|
||||
|
||||
};
|
||||
} // namespace __move_backward
|
||||
|
||||
inline namespace __cpo {
|
||||
inline constexpr auto move_backward = __move_backward::__fn{};
|
||||
} // namespace __cpo
|
||||
} // namespace ranges
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
|
||||
|
||||
#endif // _LIBCPP___ALGORITHM_RANGES_MOVE_BACKWARD_H
|
|
@ -450,6 +450,27 @@ namespace ranges {
|
|||
ranges::lexicographical_compare(R1&& r1, R2&& r2, Comp comp = {},
|
||||
Proj1 proj1 = {}, Proj2 proj2 = {}); // since C++20
|
||||
|
||||
template<bidirectional_iterator I1, sentinel_for<I1> S1, bidirectional_iterator I2>
|
||||
requires indirectly_movable<I1, I2>
|
||||
constexpr ranges::move_backward_result<I1, I2>
|
||||
ranges::move_backward(I1 first, S1 last, I2 result); // since C++20
|
||||
|
||||
template<bidirectional_range R, bidirectional_iterator I>
|
||||
requires indirectly_movable<iterator_t<R>, I>
|
||||
constexpr ranges::move_backward_result<borrowed_iterator_t<R>, I>
|
||||
ranges::move_backward(R&& r, I result); // since C++20
|
||||
|
||||
template<input_iterator I, sentinel_for<I> S, weakly_incrementable O>
|
||||
requires indirectly_movable<I, O>
|
||||
constexpr ranges::move_result<I, O>
|
||||
ranges::move(I first, S last, O result); // since C++20
|
||||
|
||||
template<input_range R, weakly_incrementable O>
|
||||
requires indirectly_movable<iterator_t<R>, O>
|
||||
constexpr ranges::move_result<borrowed_iterator_t<R>, O>
|
||||
ranges::move(R&& r, O result); // since C++20
|
||||
|
||||
|
||||
}
|
||||
|
||||
constexpr bool // constexpr in C++20
|
||||
|
@ -1195,6 +1216,8 @@ template <class BidirectionalIterator, class Compare>
|
|||
#include <__algorithm/ranges_minmax.h>
|
||||
#include <__algorithm/ranges_minmax_element.h>
|
||||
#include <__algorithm/ranges_mismatch.h>
|
||||
#include <__algorithm/ranges_move.h>
|
||||
#include <__algorithm/ranges_move_backward.h>
|
||||
#include <__algorithm/ranges_none_of.h>
|
||||
#include <__algorithm/ranges_replace.h>
|
||||
#include <__algorithm/ranges_replace_if.h>
|
||||
|
|
|
@ -338,6 +338,8 @@ module std [system] {
|
|||
module ranges_minmax { private header "__algorithm/ranges_minmax.h" }
|
||||
module ranges_minmax_element { private header "__algorithm/ranges_minmax_element.h" }
|
||||
module ranges_mismatch { private header "__algorithm/ranges_mismatch.h" }
|
||||
module ranges_move { private header "__algorithm/ranges_move.h" }
|
||||
module ranges_move_backward { private header "__algorithm/ranges_move_backward.h" }
|
||||
module ranges_none_of { private header "__algorithm/ranges_none_of.h" }
|
||||
module ranges_replace { private header "__algorithm/ranges_replace.h" }
|
||||
module ranges_replace_if { private header "__algorithm/ranges_replace_if.h" }
|
||||
|
|
|
@ -136,6 +136,8 @@ END-SCRIPT
|
|||
#include <__algorithm/ranges_minmax.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_minmax.h'}}
|
||||
#include <__algorithm/ranges_minmax_element.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_minmax_element.h'}}
|
||||
#include <__algorithm/ranges_mismatch.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_mismatch.h'}}
|
||||
#include <__algorithm/ranges_move.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_move.h'}}
|
||||
#include <__algorithm/ranges_move_backward.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_move_backward.h'}}
|
||||
#include <__algorithm/ranges_none_of.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_none_of.h'}}
|
||||
#include <__algorithm/ranges_replace.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_replace.h'}}
|
||||
#include <__algorithm/ranges_replace_if.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_replace_if.h'}}
|
||||
|
|
|
@ -0,0 +1,259 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// <algorithm>
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
|
||||
|
||||
// template<input_iterator I, sentinel_for<I> S, weakly_incrementable O>
|
||||
// requires indirectly_movable<I, O>
|
||||
// constexpr ranges::move_result<I, O>
|
||||
// ranges::move(I first, S last, O result);
|
||||
// template<input_range R, weakly_incrementable O>
|
||||
// requires indirectly_movable<iterator_t<R>, O>
|
||||
// constexpr ranges::move_result<borrowed_iterator_t<R>, O>
|
||||
// ranges::move(R&& r, O result);
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <ranges>
|
||||
|
||||
#include "almost_satisfies_types.h"
|
||||
#include "MoveOnly.h"
|
||||
#include "test_iterators.h"
|
||||
|
||||
template <class In, class Out = In, class Sent = sentinel_wrapper<In>>
|
||||
concept HasMoveIt = requires(In in, Sent sent, Out out) { std::ranges::move(in, sent, out); };
|
||||
|
||||
static_assert(HasMoveIt<int*>);
|
||||
static_assert(!HasMoveIt<InputIteratorNotDerivedFrom>);
|
||||
static_assert(!HasMoveIt<InputIteratorNotIndirectlyReadable>);
|
||||
static_assert(!HasMoveIt<InputIteratorNotInputOrOutputIterator>);
|
||||
static_assert(!HasMoveIt<int*, WeaklyIncrementableNotMovable>);
|
||||
struct NotIndirectlyMovable {};
|
||||
static_assert(!HasMoveIt<int*, NotIndirectlyMovable*>);
|
||||
static_assert(!HasMoveIt<int*, int*, SentinelForNotSemiregular>);
|
||||
static_assert(!HasMoveIt<int*, int*, SentinelForNotWeaklyEqualityComparableWith>);
|
||||
|
||||
template <class Range, class Out>
|
||||
concept HasMoveR = requires(Range range, Out out) { std::ranges::move(range, out); };
|
||||
|
||||
static_assert(HasMoveR<std::array<int, 10>, int*>);
|
||||
static_assert(!HasMoveR<InputRangeNotDerivedFrom, int*>);
|
||||
static_assert(!HasMoveR<InputRangeNotIndirectlyReadable, int*>);
|
||||
static_assert(!HasMoveR<InputRangeNotInputOrOutputIterator, int*>);
|
||||
static_assert(!HasMoveR<WeaklyIncrementableNotMovable, int*>);
|
||||
static_assert(!HasMoveR<UncheckedRange<NotIndirectlyMovable*>, int*>);
|
||||
static_assert(!HasMoveR<InputRangeNotSentinelSemiregular, int*>);
|
||||
static_assert(!HasMoveR<InputRangeNotSentinelEqualityComparableWith, int*>);
|
||||
static_assert(!HasMoveR<UncheckedRange<int*>, WeaklyIncrementableNotMovable>);
|
||||
|
||||
static_assert(std::is_same_v<std::ranges::move_result<int, long>, std::ranges::in_out_result<int, long>>);
|
||||
|
||||
template <class In, class Out, class Sent, int N>
|
||||
constexpr void test(std::array<int, N> in) {
|
||||
{
|
||||
std::array<int, N> out;
|
||||
std::same_as<std::ranges::in_out_result<In, Out>> decltype(auto) ret =
|
||||
std::ranges::move(In(in.data()), Sent(In(in.data() + in.size())), Out(out.data()));
|
||||
assert(in == out);
|
||||
assert(base(ret.in) == in.data() + in.size());
|
||||
assert(base(ret.out) == out.data() + out.size());
|
||||
}
|
||||
{
|
||||
std::array<int, N> out;
|
||||
auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data() + in.size())));
|
||||
std::same_as<std::ranges::in_out_result<In, Out>> decltype(auto) ret =
|
||||
std::ranges::move(range, Out(out.data()));
|
||||
assert(in == out);
|
||||
assert(base(ret.in) == in.data() + in.size());
|
||||
assert(base(ret.out) == out.data() + out.size());
|
||||
}
|
||||
}
|
||||
|
||||
template <class In, class Out, class Sent = In>
|
||||
constexpr void test_iterators() {
|
||||
// simple test
|
||||
test<In, Out, Sent, 4>({1, 2, 3, 4});
|
||||
// check that an empty range works
|
||||
test<In, Out, Sent, 0>({});
|
||||
}
|
||||
|
||||
template <class Out>
|
||||
constexpr void test_in_iterators() {
|
||||
test_iterators<cpp20_input_iterator<int*>, Out, sentinel_wrapper<cpp20_input_iterator<int*>>>();
|
||||
test_iterators<forward_iterator<int*>, Out>();
|
||||
test_iterators<bidirectional_iterator<int*>, Out>();
|
||||
test_iterators<random_access_iterator<int*>, Out>();
|
||||
test_iterators<contiguous_iterator<int*>, Out>();
|
||||
}
|
||||
|
||||
struct IteratorWithMoveIter {
|
||||
using value_type = int;
|
||||
using difference_type = int;
|
||||
explicit IteratorWithMoveIter() = default;
|
||||
int* ptr;
|
||||
constexpr IteratorWithMoveIter(int* ptr_) : ptr(ptr_) {}
|
||||
|
||||
constexpr int& operator*() const; // iterator with iter_move should not be dereferenced
|
||||
|
||||
constexpr IteratorWithMoveIter& operator++() { ++ptr; return *this; }
|
||||
constexpr IteratorWithMoveIter operator++(int) { auto ret = *this; ++*this; return ret; }
|
||||
|
||||
friend constexpr int iter_move(const IteratorWithMoveIter&) { return 42; }
|
||||
|
||||
constexpr bool operator==(const IteratorWithMoveIter& other) const = default;
|
||||
};
|
||||
|
||||
constexpr bool test() {
|
||||
test_in_iterators<cpp17_output_iterator<int*>>();
|
||||
test_in_iterators<cpp20_output_iterator<int*>>();
|
||||
test_in_iterators<cpp17_input_iterator<int*>>();
|
||||
test_in_iterators<cpp20_input_iterator<int*>>();
|
||||
test_in_iterators<forward_iterator<int*>>();
|
||||
test_in_iterators<bidirectional_iterator<int*>>();
|
||||
test_in_iterators<random_access_iterator<int*>>();
|
||||
test_in_iterators<contiguous_iterator<int*>>();
|
||||
|
||||
{ // check that a move-only type works
|
||||
{
|
||||
MoveOnly a[] = {1, 2, 3};
|
||||
MoveOnly b[3];
|
||||
std::ranges::move(a, std::begin(b));
|
||||
assert(b[0].get() == 1);
|
||||
assert(b[1].get() == 2);
|
||||
assert(b[2].get() == 3);
|
||||
}
|
||||
{
|
||||
MoveOnly a[] = {1, 2, 3};
|
||||
MoveOnly b[3];
|
||||
std::ranges::move(std::begin(a), std::end(a), std::begin(b));
|
||||
assert(b[0].get() == 1);
|
||||
assert(b[1].get() == 2);
|
||||
assert(b[2].get() == 3);
|
||||
}
|
||||
}
|
||||
|
||||
{ // check that ranges::dangling is returned
|
||||
std::array<int, 4> out;
|
||||
std::same_as<std::ranges::in_out_result<std::ranges::dangling, int*>> decltype(auto) ret =
|
||||
std::ranges::move(std::array {1, 2, 3, 4}, out.data());
|
||||
assert(ret.out == out.data() + 4);
|
||||
assert((out == std::array{1, 2, 3, 4}));
|
||||
}
|
||||
|
||||
{ // check that an iterator is returned with a borrowing range
|
||||
std::array in {1, 2, 3, 4};
|
||||
std::array<int, 4> out;
|
||||
std::same_as<std::ranges::in_out_result<int*, int*>> decltype(auto) ret =
|
||||
std::ranges::move(std::views::all(in), out.data());
|
||||
assert(ret.in == in.data() + 4);
|
||||
assert(ret.out == out.data() + 4);
|
||||
assert(in == out);
|
||||
}
|
||||
|
||||
{ // check that every element is moved exactly once
|
||||
struct MoveOnce {
|
||||
bool moved = false;
|
||||
constexpr MoveOnce() = default;
|
||||
constexpr MoveOnce(const MoveOnce& other) = delete;
|
||||
constexpr MoveOnce& operator=(MoveOnce&& other) {
|
||||
assert(!other.moved);
|
||||
moved = true;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
{
|
||||
std::array<MoveOnce, 4> in {};
|
||||
std::array<MoveOnce, 4> out {};
|
||||
auto ret = std::ranges::move(in.begin(), in.end(), out.begin());
|
||||
assert(ret.in == in.end());
|
||||
assert(ret.out == out.end());
|
||||
assert(std::all_of(out.begin(), out.end(), [](const auto& e) { return e.moved; }));
|
||||
}
|
||||
{
|
||||
std::array<MoveOnce, 4> in {};
|
||||
std::array<MoveOnce, 4> out {};
|
||||
auto ret = std::ranges::move(in, out.begin());
|
||||
assert(ret.in == in.end());
|
||||
assert(ret.out == out.end());
|
||||
assert(std::all_of(out.begin(), out.end(), [](const auto& e) { return e.moved; }));
|
||||
}
|
||||
}
|
||||
|
||||
{ // check that the range is moved forwards
|
||||
struct OnlyForwardsMovable {
|
||||
OnlyForwardsMovable* next = nullptr;
|
||||
bool canMove = false;
|
||||
OnlyForwardsMovable() = default;
|
||||
constexpr OnlyForwardsMovable& operator=(OnlyForwardsMovable&&) {
|
||||
assert(canMove);
|
||||
if (next != nullptr)
|
||||
next->canMove = true;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
{
|
||||
std::array<OnlyForwardsMovable, 3> in {};
|
||||
std::array<OnlyForwardsMovable, 3> out {};
|
||||
out[0].next = &out[1];
|
||||
out[1].next = &out[2];
|
||||
out[0].canMove = true;
|
||||
auto ret = std::ranges::move(in.begin(), in.end(), out.begin());
|
||||
assert(ret.in == in.end());
|
||||
assert(ret.out == out.end());
|
||||
assert(out[0].canMove);
|
||||
assert(out[1].canMove);
|
||||
assert(out[2].canMove);
|
||||
}
|
||||
{
|
||||
std::array<OnlyForwardsMovable, 3> in {};
|
||||
std::array<OnlyForwardsMovable, 3> out {};
|
||||
out[0].next = &out[1];
|
||||
out[1].next = &out[2];
|
||||
out[0].canMove = true;
|
||||
auto ret = std::ranges::move(in, out.begin());
|
||||
assert(ret.in == in.end());
|
||||
assert(ret.out == out.end());
|
||||
assert(out[0].canMove);
|
||||
assert(out[1].canMove);
|
||||
assert(out[2].canMove);
|
||||
}
|
||||
}
|
||||
|
||||
{ // check that iter_move is used properly
|
||||
{
|
||||
int a[] = {1, 2, 3, 4};
|
||||
std::array<int, 4> b;
|
||||
auto ret = std::ranges::move(IteratorWithMoveIter(a), IteratorWithMoveIter(a + 4), b.data());
|
||||
assert(ret.in == a + 4);
|
||||
assert(ret.out == b.data() + 4);
|
||||
assert((b == std::array {42, 42, 42, 42}));
|
||||
}
|
||||
{
|
||||
int a[] = {1, 2, 3, 4};
|
||||
std::array<int, 4> b;
|
||||
auto range = std::ranges::subrange(IteratorWithMoveIter(a), IteratorWithMoveIter(a + 4));
|
||||
auto ret = std::ranges::move(range, b.data());
|
||||
assert(ret.in == a + 4);
|
||||
assert(ret.out == b.data() + 4);
|
||||
assert((b == std::array {42, 42, 42, 42}));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,256 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// <algorithm>
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
|
||||
|
||||
// template<bidirectional_iterator I1, sentinel_for<I1> S1, bidirectional_iterator I2>
|
||||
// requires indirectly_movable<I1, I2>
|
||||
// constexpr ranges::move_backward_result<I1, I2>
|
||||
// ranges::move_backward(I1 first, S1 last, I2 result);
|
||||
// template<bidirectional_range R, bidirectional_iterator I>
|
||||
// requires indirectly_movable<iterator_t<R>, I>
|
||||
// constexpr ranges::move_backward_result<borrowed_iterator_t<R>, I>
|
||||
// ranges::move_backward(R&& r, I result);
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <ranges>
|
||||
|
||||
#include "almost_satisfies_types.h"
|
||||
#include "MoveOnly.h"
|
||||
#include "test_iterators.h"
|
||||
|
||||
template <class In, class Out = In, class Sent = sentinel_wrapper<In>>
|
||||
concept HasMoveBackwardIt = requires(In in, Sent sent, Out out) { std::ranges::move_backward(in, sent, out); };
|
||||
|
||||
static_assert(HasMoveBackwardIt<int*>);
|
||||
static_assert(!HasMoveBackwardIt<InputIteratorNotDerivedFrom>);
|
||||
static_assert(!HasMoveBackwardIt<InputIteratorNotIndirectlyReadable>);
|
||||
static_assert(!HasMoveBackwardIt<InputIteratorNotInputOrOutputIterator>);
|
||||
static_assert(!HasMoveBackwardIt<int*, WeaklyIncrementableNotMovable>);
|
||||
struct NotIndirectlyCopyable {};
|
||||
static_assert(!HasMoveBackwardIt<int*, NotIndirectlyCopyable*>);
|
||||
static_assert(!HasMoveBackwardIt<int*, int*, SentinelForNotSemiregular>);
|
||||
static_assert(!HasMoveBackwardIt<int*, int*, SentinelForNotWeaklyEqualityComparableWith>);
|
||||
|
||||
template <class Range, class Out>
|
||||
concept HasMoveBackwardR = requires(Range range, Out out) { std::ranges::move_backward(range, out); };
|
||||
|
||||
static_assert(HasMoveBackwardR<std::array<int, 10>, int*>);
|
||||
static_assert(!HasMoveBackwardR<InputRangeNotDerivedFrom, int*>);
|
||||
static_assert(!HasMoveBackwardR<InputRangeNotIndirectlyReadable, int*>);
|
||||
static_assert(!HasMoveBackwardR<InputRangeNotInputOrOutputIterator, int*>);
|
||||
static_assert(!HasMoveBackwardR<WeaklyIncrementableNotMovable, int*>);
|
||||
static_assert(!HasMoveBackwardR<UncheckedRange<NotIndirectlyCopyable*>, int*>);
|
||||
static_assert(!HasMoveBackwardR<InputRangeNotSentinelSemiregular, int*>);
|
||||
static_assert(!HasMoveBackwardR<InputRangeNotSentinelEqualityComparableWith, int*>);
|
||||
static_assert(!HasMoveBackwardR<UncheckedRange<int*>, WeaklyIncrementableNotMovable>);
|
||||
|
||||
static_assert(std::is_same_v<std::ranges::copy_result<int, long>, std::ranges::in_out_result<int, long>>);
|
||||
|
||||
template <class In, class Out, class Sent, int N>
|
||||
constexpr void test(std::array<int, N> in) {
|
||||
{
|
||||
std::array<int, N> out;
|
||||
std::same_as<std::ranges::in_out_result<In, Out>> decltype(auto) ret =
|
||||
std::ranges::move_backward(In(in.data()), Sent(In(in.data() + in.size())), Out(out.data() + out.size()));
|
||||
assert(in == out);
|
||||
assert(base(ret.in) == in.data());
|
||||
assert(base(ret.out) == out.data());
|
||||
}
|
||||
{
|
||||
std::array<int, N> out;
|
||||
auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data() + in.size())));
|
||||
std::same_as<std::ranges::in_out_result<In, Out>> decltype(auto) ret =
|
||||
std::ranges::move_backward(range, Out(out.data() + out.size()));
|
||||
assert(in == out);
|
||||
assert(base(ret.in) == in.data());
|
||||
assert(base(ret.out) == out.data());
|
||||
}
|
||||
}
|
||||
|
||||
template <class In, class Out, class Sent = In>
|
||||
constexpr void test_iterators() {
|
||||
// simple test
|
||||
test<In, Out, Sent, 4>({1, 2, 3, 4});
|
||||
// check that an empty range works
|
||||
test<In, Out, Sent, 0>({});
|
||||
}
|
||||
|
||||
template <class Out>
|
||||
constexpr void test_in_iterators() {
|
||||
test_iterators<bidirectional_iterator<int*>, Out, sentinel_wrapper<bidirectional_iterator<int*>>>();
|
||||
test_iterators<bidirectional_iterator<int*>, Out>();
|
||||
test_iterators<random_access_iterator<int*>, Out>();
|
||||
test_iterators<contiguous_iterator<int*>, Out>();
|
||||
}
|
||||
|
||||
struct IteratorWithMoveIter {
|
||||
using value_type = int;
|
||||
using difference_type = int;
|
||||
explicit IteratorWithMoveIter() = default;
|
||||
int* ptr;
|
||||
constexpr IteratorWithMoveIter(int* ptr_) : ptr(ptr_) {}
|
||||
|
||||
constexpr int& operator*() const; // iterator with iter_move should not be dereferenced
|
||||
|
||||
constexpr IteratorWithMoveIter& operator++() { ++ptr; return *this; }
|
||||
constexpr IteratorWithMoveIter operator++(int) { auto ret = *this; ++*this; return ret; }
|
||||
|
||||
constexpr IteratorWithMoveIter& operator--() { --ptr; return *this; }
|
||||
constexpr IteratorWithMoveIter operator--(int) { auto ret = *this; --*this; return ret; }
|
||||
|
||||
friend constexpr int iter_move(const IteratorWithMoveIter&) { return 42; }
|
||||
|
||||
constexpr bool operator==(const IteratorWithMoveIter& other) const = default;
|
||||
};
|
||||
|
||||
constexpr bool test() {
|
||||
test_in_iterators<bidirectional_iterator<int*>>();
|
||||
test_in_iterators<random_access_iterator<int*>>();
|
||||
test_in_iterators<contiguous_iterator<int*>>();
|
||||
|
||||
{ // check that a move-only type works
|
||||
{
|
||||
MoveOnly a[] = {1, 2, 3};
|
||||
MoveOnly b[3];
|
||||
std::ranges::move_backward(a, std::end(b));
|
||||
assert(b[0].get() == 1);
|
||||
assert(b[1].get() == 2);
|
||||
assert(b[2].get() == 3);
|
||||
}
|
||||
{
|
||||
MoveOnly a[] = {1, 2, 3};
|
||||
MoveOnly b[3];
|
||||
std::ranges::move_backward(std::begin(a), std::end(a), std::end(b));
|
||||
assert(b[0].get() == 1);
|
||||
assert(b[1].get() == 2);
|
||||
assert(b[2].get() == 3);
|
||||
}
|
||||
}
|
||||
|
||||
{ // check that ranges::dangling is returned
|
||||
std::array<int, 4> out;
|
||||
std::same_as<std::ranges::in_out_result<std::ranges::dangling, int*>> auto ret =
|
||||
std::ranges::move_backward(std::array {1, 2, 3, 4}, out.data() + out.size());
|
||||
assert(ret.out == out.data());
|
||||
assert((out == std::array{1, 2, 3, 4}));
|
||||
}
|
||||
|
||||
{ // check that an iterator is returned with a borrowing range
|
||||
std::array in {1, 2, 3, 4};
|
||||
std::array<int, 4> out;
|
||||
std::same_as<std::ranges::in_out_result<int*, int*>> auto ret =
|
||||
std::ranges::move_backward(std::views::all(in), out.data() + out.size());
|
||||
assert(ret.in == in.data());
|
||||
assert(ret.out == out.data());
|
||||
assert(in == out);
|
||||
}
|
||||
|
||||
{ // check that every element is moved exactly once
|
||||
struct MoveOnce {
|
||||
bool moved = false;
|
||||
constexpr MoveOnce() = default;
|
||||
constexpr MoveOnce(const MoveOnce& other) = delete;
|
||||
constexpr MoveOnce& operator=(const MoveOnce& other) {
|
||||
assert(!other.moved);
|
||||
moved = true;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
{
|
||||
std::array<MoveOnce, 4> in {};
|
||||
std::array<MoveOnce, 4> out {};
|
||||
auto ret = std::ranges::move_backward(in.begin(), in.end(), out.end());
|
||||
assert(ret.in == in.begin());
|
||||
assert(ret.out == out.begin());
|
||||
assert(std::all_of(out.begin(), out.end(), [](const auto& e) { return e.moved; }));
|
||||
}
|
||||
{
|
||||
std::array<MoveOnce, 4> in {};
|
||||
std::array<MoveOnce, 4> out {};
|
||||
auto ret = std::ranges::move_backward(in, out.end());
|
||||
assert(ret.in == in.begin());
|
||||
assert(ret.out == out.begin());
|
||||
assert(std::all_of(out.begin(), out.end(), [](const auto& e) { return e.moved; }));
|
||||
}
|
||||
}
|
||||
|
||||
{ // check that the range is moved backwards
|
||||
struct OnlyBackwardsMovable {
|
||||
OnlyBackwardsMovable* next = nullptr;
|
||||
bool canMove = false;
|
||||
OnlyBackwardsMovable() = default;
|
||||
constexpr OnlyBackwardsMovable& operator=(const OnlyBackwardsMovable&) {
|
||||
assert(canMove);
|
||||
if (next != nullptr)
|
||||
next->canMove = true;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
{
|
||||
std::array<OnlyBackwardsMovable, 3> in {};
|
||||
std::array<OnlyBackwardsMovable, 3> out {};
|
||||
out[1].next = &out[0];
|
||||
out[2].next = &out[1];
|
||||
out[2].canMove = true;
|
||||
auto ret = std::ranges::move_backward(in, out.end());
|
||||
assert(ret.in == in.begin());
|
||||
assert(ret.out == out.begin());
|
||||
assert(out[0].canMove);
|
||||
assert(out[1].canMove);
|
||||
assert(out[2].canMove);
|
||||
}
|
||||
{
|
||||
std::array<OnlyBackwardsMovable, 3> in {};
|
||||
std::array<OnlyBackwardsMovable, 3> out {};
|
||||
out[1].next = &out[0];
|
||||
out[2].next = &out[1];
|
||||
out[2].canMove = true;
|
||||
auto ret = std::ranges::move_backward(in.begin(), in.end(), out.end());
|
||||
assert(ret.in == in.begin());
|
||||
assert(ret.out == out.begin());
|
||||
assert(out[0].canMove);
|
||||
assert(out[1].canMove);
|
||||
assert(out[2].canMove);
|
||||
}
|
||||
}
|
||||
|
||||
{ // check that iter_move is used properly
|
||||
{
|
||||
int a[] = {1, 2, 3, 4};
|
||||
std::array<int, 4> b;
|
||||
auto ret = std::ranges::move_backward(IteratorWithMoveIter(a), IteratorWithMoveIter(a + 4), b.data() + b.size());
|
||||
assert(ret.in == a);
|
||||
assert(ret.out == b.data());
|
||||
assert((b == std::array {42, 42, 42, 42}));
|
||||
}
|
||||
{
|
||||
int a[] = {1, 2, 3, 4};
|
||||
std::array<int, 4> b;
|
||||
auto range = std::ranges::subrange(IteratorWithMoveIter(a), IteratorWithMoveIter(a + 4));
|
||||
auto ret = std::ranges::move_backward(range, b.data() + b.size());
|
||||
assert(ret.in == a);
|
||||
assert(ret.out == b.data());
|
||||
assert((b == std::array {42, 42, 42, 42}));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -104,8 +104,8 @@ static_assert(test(std::ranges::min_element, a));
|
|||
static_assert(test(std::ranges::minmax, a));
|
||||
static_assert(test(std::ranges::minmax_element, a));
|
||||
static_assert(test(std::ranges::mismatch, a, a));
|
||||
//static_assert(test(std::ranges::move, a, a));
|
||||
//static_assert(test(std::ranges::move_backward, a, a));
|
||||
static_assert(test(std::ranges::move, a, a));
|
||||
static_assert(test(std::ranges::move_backward, a, a));
|
||||
//static_assert(test(std::ranges::next_permutation, a));
|
||||
static_assert(test(std::ranges::none_of, a, odd));
|
||||
//static_assert(test(std::ranges::nth_element, a, a+5));
|
||||
|
|
Loading…
Reference in New Issue