зеркало из https://github.com/microsoft/STL.git
Implement ranges::move_backward (#1053)
* Implement ranges::move_backward Co-authored-by: Stephan T. Lavavej <stl@nuwen.net> Co-authored-by: mnatsuhara <46756417+mnatsuhara@users.noreply.github.com>
This commit is contained in:
Родитель
3659f3dc18
Коммит
868cf267a0
|
@ -1888,6 +1888,58 @@ namespace ranges {
|
|||
};
|
||||
|
||||
inline constexpr _Move_fn move{_Not_quite_object::_Construct_tag{}};
|
||||
|
||||
// ALIAS TEMPLATE move_backward_result
|
||||
template <class _In, class _Out>
|
||||
using move_backward_result = in_out_result<_In, _Out>;
|
||||
|
||||
// VARIABLE ranges::move_backward
|
||||
// clang-format off
|
||||
// concept-constrained for strict enforcement as it is used by several algorithms
|
||||
template <bidirectional_iterator _It1, bidirectional_iterator _It2>
|
||||
requires indirectly_movable<_It1, _It2>
|
||||
_NODISCARD constexpr _It2 _Move_backward_common(const _It1 _First, _It1 _Last, _It2 _Result) {
|
||||
if constexpr (_Ptr_move_cat<_It1, _It2>::_Trivially_copyable) {
|
||||
if (!_STD is_constant_evaluated()) {
|
||||
return _Copy_backward_memmove(_First, _Last, _Result);
|
||||
}
|
||||
}
|
||||
|
||||
while (_First != _Last) {
|
||||
*--_Result = _RANGES iter_move(--_Last);
|
||||
}
|
||||
|
||||
return _Result;
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
class _Move_backward_fn : private _Not_quite_object {
|
||||
public:
|
||||
using _Not_quite_object::_Not_quite_object;
|
||||
|
||||
// clang-format off
|
||||
template <bidirectional_iterator _It1, sentinel_for<_It1> _Se1, bidirectional_iterator _It2>
|
||||
requires indirectly_movable<_It1, _It2>
|
||||
constexpr move_backward_result<_It1, _It2> operator()(_It1 _First, _Se1 _Last, _It2 _Result) const {
|
||||
_Adl_verify_range(_First, _Last);
|
||||
auto _UFirst = _Get_unwrapped(_STD move(_First));
|
||||
auto _ULast = _Get_final_iterator_unwrapped<_It1>(_UFirst, _STD move(_Last));
|
||||
_Seek_wrapped(_First, _ULast);
|
||||
_Result = _RANGES _Move_backward_common(_STD move(_UFirst), _STD move(_ULast), _STD move(_Result));
|
||||
return {_STD move(_First), _STD move(_Result)};
|
||||
}
|
||||
|
||||
template <bidirectional_range _Rng, bidirectional_iterator _It>
|
||||
requires indirectly_movable<iterator_t<_Rng>, _It>
|
||||
constexpr move_backward_result<borrowed_iterator_t<_Rng>, _It> operator()(_Rng&& _Range, _It _Result) const {
|
||||
auto _ULast = _Get_final_iterator_unwrapped(_Range);
|
||||
_Result = _RANGES _Move_backward_common(_Ubegin(_Range), _ULast, _STD move(_Result));
|
||||
return {_Rewrap_iterator(_Range, _STD move(_ULast)), _STD move(_Result)};
|
||||
}
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
inline constexpr _Move_backward_fn move_backward{_Not_quite_object::_Construct_tag{}};
|
||||
} // namespace ranges
|
||||
#endif // __cpp_lib_concepts
|
||||
|
||||
|
|
|
@ -267,6 +267,7 @@ tests\P0896R4_ranges_alg_is_sorted
|
|||
tests\P0896R4_ranges_alg_minmax
|
||||
tests\P0896R4_ranges_alg_mismatch
|
||||
tests\P0896R4_ranges_alg_move
|
||||
tests\P0896R4_ranges_alg_move_backward
|
||||
tests\P0896R4_ranges_alg_none_of
|
||||
tests\P0896R4_ranges_alg_partition
|
||||
tests\P0896R4_ranges_alg_partition_copy
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
RUNALL_INCLUDE ..\concepts_matrix.lst
|
|
@ -0,0 +1,141 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <ranges>
|
||||
#include <span>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <range_algorithm_support.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct int_wrapper {
|
||||
int val = 10;
|
||||
|
||||
constexpr int_wrapper() = default;
|
||||
constexpr int_wrapper(int x) : val{x} {}
|
||||
constexpr int_wrapper(int_wrapper&& that) : val{exchange(that.val, -1)} {}
|
||||
constexpr int_wrapper& operator=(int_wrapper&& that) {
|
||||
val = exchange(that.val, -1);
|
||||
return *this;
|
||||
}
|
||||
auto operator<=>(const int_wrapper&) const = default;
|
||||
};
|
||||
|
||||
// Validate that move_backward_result aliases in_out_result
|
||||
STATIC_ASSERT(same_as<ranges::move_backward_result<int, double>, ranges::in_out_result<int, double>>);
|
||||
|
||||
// Validate dangling story
|
||||
STATIC_ASSERT(same_as<decltype(ranges::move_backward(borrowed<false>{}, nullptr_to<int>)),
|
||||
ranges::move_backward_result<ranges::dangling, int*>>);
|
||||
STATIC_ASSERT(same_as<decltype(ranges::move_backward(borrowed<true>{}, nullptr_to<int>)),
|
||||
ranges::move_backward_result<int*, int*>>);
|
||||
|
||||
struct instantiator {
|
||||
static constexpr int expected_output[] = {13, 42, 1729};
|
||||
static constexpr int expected_input[] = {-1, -1, -1};
|
||||
static constexpr int expected_overlapping[] = {-1, 0, 1, 2};
|
||||
|
||||
template <ranges::bidirectional_range R1, ranges::bidirectional_range R2>
|
||||
static constexpr void call() {
|
||||
#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163
|
||||
#pragma warning(suppress : 4127) // conditional expression is constant
|
||||
if (!ranges::contiguous_range<R1> || !is_constant_evaluated())
|
||||
#endif // TRANSITION, VSO-938163
|
||||
{
|
||||
// For the second range, we need an iterator to the end; it's expedient to simply ignore ranges with
|
||||
// differing iterator and sentinel types (i.e., ranges that don't satisfy common_range).
|
||||
if constexpr (ranges::common_range<R2>) {
|
||||
using ranges::move_backward, ranges::move_backward_result, ranges::equal, ranges::iterator_t;
|
||||
|
||||
{ // Validate range overload
|
||||
int_wrapper input[] = {13, 42, 1729};
|
||||
int_wrapper output[] = {-2, -2, -2};
|
||||
R1 wrapped_input{input};
|
||||
R2 wrapped_output{output};
|
||||
same_as<move_backward_result<iterator_t<R1>, iterator_t<R2>>> auto result =
|
||||
move_backward(wrapped_input, wrapped_output.end());
|
||||
assert(result.in == wrapped_input.end());
|
||||
assert(result.out == wrapped_output.begin());
|
||||
assert(equal(output, expected_output, ranges::equal_to{}, &int_wrapper::val));
|
||||
assert(equal(input, expected_input, ranges::equal_to{}, &int_wrapper::val));
|
||||
}
|
||||
{ // Validate iterator + sentinel overload
|
||||
int_wrapper input[] = {13, 42, 1729};
|
||||
int_wrapper output[] = {-2, -2, -2};
|
||||
R1 wrapped_input{input};
|
||||
R2 wrapped_output{output};
|
||||
same_as<move_backward_result<iterator_t<R1>, iterator_t<R2>>> auto result =
|
||||
move_backward(wrapped_input.begin(), wrapped_input.end(), wrapped_output.end());
|
||||
assert(result.in == wrapped_input.end());
|
||||
assert(result.out == wrapped_output.begin());
|
||||
assert(equal(output, expected_output, ranges::equal_to{}, &int_wrapper::val));
|
||||
assert(equal(input, expected_input, ranges::equal_to{}, &int_wrapper::val));
|
||||
}
|
||||
{ // Validate overlapping ranges
|
||||
int_wrapper io[] = {0, 1, 2, 42};
|
||||
R1 wrapped_input{span{io}.first<3>()};
|
||||
R2 wrapped_output{span{io}.last<3>()};
|
||||
same_as<move_backward_result<iterator_t<R1>, iterator_t<R2>>> auto result =
|
||||
move_backward(wrapped_input, wrapped_output.end());
|
||||
assert(result.in == wrapped_input.end());
|
||||
assert(result.out == wrapped_output.begin());
|
||||
assert(equal(io, expected_overlapping, ranges::equal_to{}, &int_wrapper::val));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
constexpr void test_memmove() {
|
||||
// Get some coverage for the memmove optimization, which we would not otherwise have since we do not currently
|
||||
// unwrap output iterators. TRANSITION, GH-893
|
||||
using ranges::move_backward, ranges::move_backward_result, ranges::begin, ranges::end, ranges::equal;
|
||||
|
||||
struct S { // move-only and trivially copyable
|
||||
int val = 10;
|
||||
|
||||
constexpr S() = default;
|
||||
constexpr S(int x) : val{x} {}
|
||||
constexpr S(S&&) = default;
|
||||
constexpr S& operator=(S&&) = default;
|
||||
auto operator<=>(const S&) const = default;
|
||||
};
|
||||
|
||||
{ // Validate range overload
|
||||
S input[] = {13, 42, 1729};
|
||||
S output[] = {-2, -2, -2};
|
||||
const same_as<move_backward_result<S*, S*>> auto result = move_backward(input, end(output));
|
||||
assert(result.in == end(input));
|
||||
assert(result.out == begin(output));
|
||||
assert(equal(output, input));
|
||||
}
|
||||
{ // Validate iterator + sentinel overload
|
||||
S input[] = {13, 42, 1729};
|
||||
S output[] = {-2, -2, -2};
|
||||
const same_as<move_backward_result<S*, S*>> auto result = move_backward(begin(input), end(input), end(output));
|
||||
assert(result.in == end(input));
|
||||
assert(result.out == begin(output));
|
||||
assert(equal(output, input));
|
||||
}
|
||||
{ // Validate overlapping ranges
|
||||
S io[] = {0, 1, 2, 42};
|
||||
const same_as<move_backward_result<S*, S*>> auto result = move_backward(io + 0, io + 3, io + 4);
|
||||
assert(result.in == io + 3);
|
||||
assert(result.out == io + 1);
|
||||
constexpr int expected[] = {0, 0, 1, 2};
|
||||
assert(equal(io, expected, ranges::equal_to{}, &S::val));
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
STATIC_ASSERT((test_bidi_bidi<instantiator, int_wrapper, int_wrapper>(), true));
|
||||
test_bidi_bidi<instantiator, int_wrapper, int_wrapper>();
|
||||
|
||||
STATIC_ASSERT((test_memmove(), true));
|
||||
test_memmove();
|
||||
}
|
Загрузка…
Ссылка в новой задаче