Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -1789,6 +1789,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

Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,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
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_move_backward/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\concepts_matrix.lst
141 changes: 141 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_move_backward/test.cpp
Original file line number Diff line number Diff line change
@@ -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();
}