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:
Casey Carter 2020-07-28 15:31:55 -07:00 коммит произвёл GitHub
Родитель 3659f3dc18
Коммит 868cf267a0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 198 добавлений и 0 удалений

Просмотреть файл

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