//===----------------------------------------------------------------------===// // // 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, class T, output_iterator O, // class Proj = identity, indirect_unary_predicate> Pred> // requires indirectly_copyable // constexpr replace_copy_if_result // replace_copy_if(I first, S last, O result, Pred pred, const T& new_value, // Proj proj = {}); // Since C++20 // // template O, class Proj = identity, // indirect_unary_predicate, Proj>> Pred> // requires indirectly_copyable, O> // constexpr replace_copy_if_result, O> // replace_copy_if(R&& r, O result, Pred pred, const T& new_value, // Proj proj = {}); // Since C++20 #include #include #include #include #include #include #include "almost_satisfies_types.h" #include "counting_predicates.h" #include "counting_projection.h" #include "test_iterators.h" struct FalsePredicate { constexpr bool operator()(int) { return false; } }; template , class OutIter = int*> concept HasReplaceCopyIfIter = requires(Iter&& first, Sent&& last, OutIter&& result) { std::ranges::replace_copy_if( std::forward(first), std::forward(last), std::forward(result), FalsePredicate{}, 0); }; static_assert(HasReplaceCopyIfIter); // !input_iterator static_assert(!HasReplaceCopyIfIter); static_assert(!HasReplaceCopyIfIter); static_assert(!HasReplaceCopyIfIter); // !sentinel_for static_assert(!HasReplaceCopyIfIter); static_assert(!HasReplaceCopyIfIter); // !output_iterator static_assert(!HasReplaceCopyIfIter); static_assert(!HasReplaceCopyIfIter); // !indirect_unary_predicate> Pred> static_assert(!HasReplaceCopyIfIter); static_assert(!HasReplaceCopyIfIter); // !indirectly_copyable static_assert(!HasReplaceCopyIfIter); template concept HasReplaceCopyIfRange = requires(Range&& range, OutIter&& result) { std::ranges::replace_copy_if(std::forward(range), std::forward(result), FalsePredicate{}, 0); }; template using R = UncheckedRange; static_assert(HasReplaceCopyIfRange>); // !input_range static_assert(!HasReplaceCopyIfRange); static_assert(!HasReplaceCopyIfRange); static_assert(!HasReplaceCopyIfRange); static_assert(!HasReplaceCopyIfRange); static_assert(!HasReplaceCopyIfRange); // !output_iterator static_assert(!HasReplaceCopyIfRange, OutputIteratorNotIndirectlyWritable>); static_assert(!HasReplaceCopyIfRange, OutputIteratorNotInputOrOutputIterator>); // !indirect_unary_predicate, Proj>> Pred> static_assert(!HasReplaceCopyIfRange>); // !indirectly_copyable, O> static_assert(!HasReplaceCopyIfRange, int**>); template struct Data { std::array input; int cutoff; int new_value; std::array expected; }; template constexpr void test(Data d) { { // iterator overload std::array output; auto first = InIter(d.input.data()); auto last = Sent(InIter(d.input.data() + d.input.size())); auto result = OutIter(output.data()); auto pred = [&](int i) { return i < d.cutoff; }; std::same_as> decltype(auto) ret = std::ranges::replace_copy_if(std::move(first), std::move(last), std::move(result), pred, d.new_value); assert(base(ret.in) == d.input.data() + d.input.size()); assert(base(ret.out) == output.data() + output.size()); 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()))); auto result = OutIter(output.data()); auto pred = [&](int i) { return i < d.cutoff; }; std::same_as> decltype(auto) ret = std::ranges::replace_copy_if(range, result, pred, d.new_value); assert(base(ret.in) == d.input.data() + d.input.size()); assert(base(ret.out) == output.data() + output.size()); assert(d.expected == output); } } template constexpr void tests() { // simple test test({.input = {1, 2, 3, 4}, .cutoff = 2, .new_value = 5, .expected = {5, 2, 3, 4}}); // empty range test({.input = {}, .cutoff = 2, .new_value = 5, .expected = {}}); // all elements match test({.input = {1, 1, 1, 1}, .cutoff = 2, .new_value = 2, .expected = {2, 2, 2, 2}}); // no element matches test({.input = {1, 1, 1, 1}, .cutoff = 1, .new_value = 3, .expected = {1, 1, 1, 1}}); // more elements test( {.input = {1, 2, 3, 4, 5, 6, 7}, .cutoff = 3, .new_value = 3, .expected = {3, 3, 3, 4, 5, 6, 7}}); // single element - match test({.input = {1}, .cutoff = 2, .new_value = 5, .expected = {5}}); // single element - no match test({.input = {2}, .cutoff = 2, .new_value = 5, .expected = {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, sentinel_wrapper>>(); test_sentinels>(); test_sentinels>(); test_sentinels>(); test_sentinels>(); test_sentinels(); test_sentinels(); { // check that a custom projection works struct S { int i; }; { // iterator overload S a[] = {{1}, {2}, {3}, {4}}; S b[4]; auto ret = std::ranges::replace_copy_if(std::begin(a), std::end(a), std::begin(b), FalsePredicate{}, S{2}, &S::i); assert(ret.in == std::end(a)); assert(ret.out == std::end(b)); assert(std::ranges::equal(a, b, {}, &S::i, &S::i)); } { // range overload S a[] = {{1}, {2}, {3}, {4}}; S b[4]; auto ret = std::ranges::replace_copy_if(a, std::begin(b), FalsePredicate{}, S{2}, &S::i); assert(ret.in == std::end(a)); assert(ret.out == std::end(b)); assert(std::ranges::equal(a, b, {}, &S::i, &S::i)); } } { // Complexity: exactly `last - first` applications of the corresponding predicate and any projection. { // iterator overload int pred_count = 0; int proj_count = 0; int a[] = {1, 2, 3, 4}; int b[4]; std::ranges::replace_copy_if( std::begin(a), std::end(a), std::begin(b), counting_predicate(FalsePredicate{}, pred_count), 0, counting_projection(proj_count)); assert(pred_count == 4); assert(proj_count == 4); } { // range overload int pred_count = 0; int proj_count = 0; int a[] = {1, 2, 3, 4}; int b[4]; std::ranges::replace_copy_if(a, std::begin(b), counting_predicate(FalsePredicate{}, pred_count), 0, counting_projection(proj_count)); assert(pred_count == 4); assert(proj_count == 4); } } { // using different types for the old and new values works struct S { constexpr operator int() const { return 1; } }; { int a[] = {0, 0, 2, 3}; int b[4]; std::ranges::replace_copy_if(std::begin(a), std::end(a), std::begin(b), [](int i) { return i < 2; }, S{}); assert(std::ranges::equal(b, std::array{1, 1, 2, 3})); } { int a[] = {0, 0, 2, 3}; int b[4]; std::ranges::replace_copy_if(a, std::begin(b), [](int i) { return i < 2; }, S{}); assert(std::ranges::equal(b, std::array{1, 1, 2, 3})); } } return true; } int main(int, char**) { test(); static_assert(test()); return 0; }