//===----------------------------------------------------------------------===// // // 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 S, weakly_incrementable O> // requires indirectly_movable // constexpr ranges::move_result // ranges::move(I first, S last, O result); // template // requires indirectly_movable, O> // constexpr ranges::move_result, O> // ranges::move(R&& r, O result); #include #include #include #include #include "almost_satisfies_types.h" #include "MoveOnly.h" #include "test_iterators.h" template > concept HasMoveIt = requires(In in, Sent sent, Out out) { std::ranges::move(in, sent, out); }; static_assert(HasMoveIt); static_assert(!HasMoveIt); static_assert(!HasMoveIt); static_assert(!HasMoveIt); static_assert(!HasMoveIt); struct NotIndirectlyMovable {}; static_assert(!HasMoveIt); static_assert(!HasMoveIt); static_assert(!HasMoveIt); template concept HasMoveR = requires(Range range, Out out) { std::ranges::move(range, out); }; static_assert(HasMoveR, int*>); static_assert(!HasMoveR); static_assert(!HasMoveR); static_assert(!HasMoveR); static_assert(!HasMoveR); static_assert(!HasMoveR, int*>); static_assert(!HasMoveR); static_assert(!HasMoveR); static_assert(!HasMoveR, 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(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 out; auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data() + in.size()))); std::same_as> 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 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>(); test_iterators, Out>(); test_iterators(); } template constexpr void test_proxy_in_iterators() { test_iterators>, Out, sentinel_wrapper>>>(); test_iterators>, Out>(); 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; } 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_in_iterators>(); test_in_iterators>(); test_in_iterators>(); test_in_iterators>(); test_in_iterators>(); test_in_iterators(); test_proxy_in_iterators>>(); test_proxy_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(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 a move-only type works for ProxyIterator { MoveOnly a[] = {1, 2, 3}; MoveOnly b[3]; ProxyRange proxyA{a}; ProxyRange proxyB{b}; std::ranges::move(proxyA, std::begin(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(std::begin(proxyA), std::end(proxyA), std::begin(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> 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 out; std::same_as> 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 in {}; std::array 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 in {}; std::array 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 in {}; std::array 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 in {}; std::array 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 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 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; }