//===----------------------------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// // // UNSUPPORTED: c++03, c++11, c++14, c++17 // template S1, bidirectional_iterator I2> // requires indirectly_movable // constexpr ranges::move_backward_result // ranges::move_backward(I1 first, S1 last, I2 result); // template // requires indirectly_movable, I> // constexpr ranges::move_backward_result, I> // ranges::move_backward(R&& r, I result); #include #include #include #include #include "almost_satisfies_types.h" #include "MoveOnly.h" #include "test_iterators.h" template > concept HasMoveBackwardIt = requires(In in, Sent sent, Out out) { std::ranges::move_backward(in, sent, out); }; static_assert(HasMoveBackwardIt); static_assert(!HasMoveBackwardIt); static_assert(!HasMoveBackwardIt); static_assert(!HasMoveBackwardIt); static_assert(!HasMoveBackwardIt); struct NotIndirectlyCopyable {}; static_assert(!HasMoveBackwardIt); static_assert(!HasMoveBackwardIt); static_assert(!HasMoveBackwardIt); template concept HasMoveBackwardR = requires(Range range, Out out) { std::ranges::move_backward(range, out); }; static_assert(HasMoveBackwardR, int*>); static_assert(!HasMoveBackwardR); static_assert(!HasMoveBackwardR); static_assert(!HasMoveBackwardR); static_assert(!HasMoveBackwardR); static_assert(!HasMoveBackwardR, int*>); static_assert(!HasMoveBackwardR); static_assert(!HasMoveBackwardR); static_assert(!HasMoveBackwardR, WeaklyIncrementableNotMovable>); static_assert(std::is_same_v, std::ranges::in_out_result>); template constexpr void test(std::array in) { { std::array out; std::same_as> 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() + in.size()); assert(base(ret.out) == out.data()); } { std::array out; auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data() + in.size()))); std::same_as> decltype(auto) ret = std::ranges::move_backward(range, Out(out.data() + out.size())); assert(in == out); assert(base(ret.in) == in.data() + in.size()); assert(base(ret.out) == out.data()); } } template constexpr void test_iterators() { // simple test test({1, 2, 3, 4}); // check that an empty range works test({}); } template constexpr void test_in_iterators() { test_iterators, Out, sentinel_wrapper>>(); test_iterators, Out>(); test_iterators, Out>(); test_iterators, Out>(); } template constexpr void test_proxy_in_iterators() { test_iterators>, Out, sentinel_wrapper>>>(); test_iterators>, Out>(); test_iterators>, Out>(); test_iterators>, 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>(); test_in_iterators>(); test_in_iterators>(); test_proxy_in_iterators>>(); test_proxy_in_iterators>>(); test_proxy_in_iterators>>(); { // 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 a move-only type works for ProxyIterator { MoveOnly a[] = {1, 2, 3}; MoveOnly b[3]; ProxyRange proxyA{a}; ProxyRange proxyB{b}; std::ranges::move_backward(proxyA, std::ranges::next(proxyB.begin(), std::end(proxyB))); assert(b[0].get() == 1); assert(b[1].get() == 2); assert(b[2].get() == 3); } { MoveOnly a[] = {1, 2, 3}; MoveOnly b[3]; ProxyRange proxyA{a}; ProxyRange proxyB{b}; std::ranges::move_backward(std::begin(proxyA), std::end(proxyA), std::ranges::next(proxyB.begin(), std::end(proxyB))); assert(b[0].get() == 1); assert(b[1].get() == 2); assert(b[2].get() == 3); } } { // check that ranges::dangling is returned std::array out; std::same_as> 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 out; std::same_as> auto ret = std::ranges::move_backward(std::views::all(in), out.data() + out.size()); assert(ret.in == in.data() + in.size()); 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 in {}; std::array out {}; auto ret = std::ranges::move_backward(in.begin(), in.end(), out.end()); assert(ret.in == in.end()); assert(ret.out == out.begin()); assert(std::all_of(out.begin(), out.end(), [](const auto& e) { return e.moved; })); } { std::array in {}; std::array out {}; auto ret = std::ranges::move_backward(in, out.end()); assert(ret.in == in.end()); 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 in {}; std::array 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.end()); assert(ret.out == out.begin()); assert(out[0].canMove); assert(out[1].canMove); assert(out[2].canMove); } { std::array in {}; std::array 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.end()); 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 b; auto ret = std::ranges::move_backward(IteratorWithMoveIter(a), IteratorWithMoveIter(a + 4), b.data() + b.size()); assert(ret.in == a + 4); assert(ret.out == b.data()); assert((b == std::array {42, 42, 42, 42})); } { int a[] = {1, 2, 3, 4}; std::array 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 + 4); 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; }