//===----------------------------------------------------------------------===// // // 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, class T, // class Proj = identity> // requires indirectly_copyable && // indirect_binary_predicate, const T*> // constexpr remove_copy_result // remove_copy(I first, S last, O result, const T& value, Proj proj = {}); // Since C++20 // // template // requires indirectly_copyable, O> && // indirect_binary_predicate, Proj>, const T*> // constexpr remove_copy_result, O> // remove_copy(R&& r, O result, const T& value, Proj proj = {}); // Since C++20 #include #include #include #include #include #include #include "almost_satisfies_types.h" #include "counting_projection.h" #include "test_iterators.h" struct ToPtr { int* operator()(int) const; }; template concept HasRemoveCopyIter = requires(Iter&& iter, Sent&& sent, OutIter&& out, Proj&& proj) { std::ranges::remove_copy( std::forward(iter), std::forward(sent), std::forward(out), 0, std::forward(proj)); }; static_assert(HasRemoveCopyIter); // !input_iterator static_assert(!HasRemoveCopyIter); static_assert(!HasRemoveCopyIter>); // !sentinel_for static_assert(!HasRemoveCopyIter); static_assert(!HasRemoveCopyIter); // !weakly_incrementable static_assert(!HasRemoveCopyIter); // !indirect_binary_predicate, const T*> static_assert(!HasRemoveCopyIter); // !indirectly_copyable static_assert(!HasRemoveCopyIter); static_assert(!HasRemoveCopyIter); template concept HasRemoveCopyRange = requires(Range&& range, OutIter&& out, Proj&& proj) { std::ranges::remove_copy( std::forward(range), std::forward(out), 0, std::forward(proj)); }; template using R = UncheckedRange; static_assert(HasRemoveCopyRange>); // !input_range static_assert(!HasRemoveCopyRange); static_assert(!HasRemoveCopyRange); static_assert(!HasRemoveCopyRange); static_assert(!HasRemoveCopyRange); static_assert(!HasRemoveCopyRange); // !weakly_incrementable static_assert(!HasRemoveCopyRange, WeaklyIncrementableNotMovable>); // !indirect_binary_predicate, Proj>, const T*> static_assert(!HasRemoveCopyRange, int*, ToPtr>); // !indirectly_copyable static_assert(!HasRemoveCopyRange, int*, OutputIteratorNotIndirectlyWritable>); static_assert(!HasRemoveCopyRange); template struct Data { std::array input; std::array expected; int val; }; template constexpr void test(Data d) { using Result = std::ranges::remove_copy_result; { // iterator overload std::array output; std::same_as decltype(auto) ret = std::ranges::remove_copy( InIter(d.input.data()), Sent(InIter(d.input.data() + d.input.size())), OutIter(output.data()), d.val); assert(base(ret.in) == d.input.data() + N); assert(base(ret.out) == output.data() + M); assert(d.expected == output); } { // range overload std::array output; auto range = std::ranges::subrange(InIter(d.input.data()), Sent(InIter(d.input.data() + d.input.size()))); std::same_as decltype(auto) ret = std::ranges::remove_copy(range, OutIter(output.data()), d.val); assert(base(ret.in) == d.input.data() + N); assert(base(ret.out) == output.data() + M); assert(d.expected == output); } } template constexpr void tests() { // simple test test({.input = {1, 2, 3, 4, 5, 6}, .expected = {1, 2, 3, 4, 6}, .val = 5}); // empty range test({}); // single element range - match test({.input = {1}, .expected = {}, .val = 1}); // single element range - no match test({.input = {1}, .expected = {1}, .val = 2}); // two element range - same order test({.input = {1, 2}, .expected = {1}, .val = 2}); // two element range - reversed order test({.input = {1, 2}, .expected = {2}, .val = 1}); // all elements match test({.input = {1, 1, 1, 1, 1}, .expected = {}, .val = 1}); // the relative order of elements isn't changed test({.input = {1, 2, 3, 2, 3, 4, 2, 5}, .expected = {1, 3, 3, 4, 5}, .val = 2}); } template constexpr void test_output_iterators() { tests>(); tests>(); tests>(); tests>(); tests>(); tests(); } template constexpr void test_sentinels() { test_output_iterators(); test_output_iterators>(); test_output_iterators>(); } constexpr bool test() { test_output_iterators, sentinel_wrapper>>(); test_output_iterators, sized_sentinel>>(); test_output_iterators, sentinel_wrapper>>(); test_output_iterators, sized_sentinel>>(); test_sentinels>(); test_sentinels>(); test_sentinels>(); test_sentinels>(); test_sentinels(); { // check that passing a different type works struct S { constexpr operator int() const { return 3; } }; { // iterator overload int a[] = {1, 2, 3, 4}; int b[3]; std::ranges::remove_copy(std::begin(a), std::end(a), std::begin(b), S{}); } { // range overload int a[] = {1, 2, 3, 4}; int b[3]; std::ranges::remove_copy(a, std::begin(b), S{}); } } { // check that a custom projection works struct S { constexpr operator int() const { return 3; } }; { // iterator overload int a[] = {1, 2, 3, 4}; int b[3]; std::ranges::remove_copy(std::begin(a), std::end(a), std::begin(b), S{}); } { // range overload int a[] = {1, 2, 3, 4}; int b[3]; std::ranges::remove_copy(a, std::begin(b), S{}); } } // Complexity: Exactly last - first applications of the corresponding predicate and any projection. { std::array in{4, 4, 5, 6}; std::array expected{5, 6}; // iterator overload { int numberOfProj = 0; std::array out; std::ranges::remove_copy( in.begin(), in.end(), out.begin(), 4, counting_projection(numberOfProj)); assert(numberOfProj == static_cast(in.size())); assert(std::ranges::equal(out, expected)); } // range overload { int numberOfProj = 0; std::array out; std::ranges::remove_copy( in, out.begin(), 4, counting_projection(numberOfProj)); assert(numberOfProj == static_cast(in.size())); assert(std::ranges::equal(out, expected)); } } return true; } int main(int, char**) { test(); static_assert(test()); return 0; }