//===----------------------------------------------------------------------===// // // 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 T1, class T2, // output_iterator O, class Proj = identity> // requires indirectly_copyable && // indirect_binary_predicate, const T1*> // constexpr replace_copy_result // replace_copy(I first, S last, O result, const T1& old_value, const T2& new_value, // Proj proj = {}); // Since C++20 // // template O, // class Proj = identity> // requires indirectly_copyable, O> && // indirect_binary_predicate, Proj>, const T1*> // constexpr replace_copy_result, O> // replace_copy(R&& r, O result, const T1& old_value, const T2& new_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" template , class OutIter = int*> concept HasReplaceCopyIter = requires(Iter&& first, Sent&& last, OutIter&& result) { std::ranges::replace_copy( std::forward(first), std::forward(last), std::forward(result), 0, 0); }; static_assert(HasReplaceCopyIter); // !input_iterator static_assert(!HasReplaceCopyIter); static_assert(!HasReplaceCopyIter); static_assert(!HasReplaceCopyIter); // !sentinel_for static_assert(!HasReplaceCopyIter); static_assert(!HasReplaceCopyIter); // !output_iterator static_assert(!HasReplaceCopyIter); static_assert(!HasReplaceCopyIter); // !indirectly_copyable static_assert(!HasReplaceCopyIter); // !indirect_binary_predicate, const T1*> static_assert(!HasReplaceCopyIter); template concept HasReplaceCopyRange = requires(Range&& range, OutIter&& result) { std::ranges::replace_copy(std::forward(range), std::forward(result), 0, 0); }; template using R = UncheckedRange; static_assert(HasReplaceCopyRange>); // !input_range static_assert(!HasReplaceCopyRange); static_assert(!HasReplaceCopyRange); static_assert(!HasReplaceCopyRange); static_assert(!HasReplaceCopyRange); static_assert(!HasReplaceCopyRange); // !output_iterator static_assert(!HasReplaceCopyRange, OutputIteratorNotIndirectlyWritable>); static_assert(!HasReplaceCopyRange, OutputIteratorNotInputOrOutputIterator>); // !indirectly_copyable, O> static_assert(!HasReplaceCopyRange, R>); // !indirect_binary_predicate, Proj>, const T1*> static_assert(!HasReplaceCopyRange>); template struct Data { std::array input; int old_value; 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()); std::same_as> decltype(auto) ret = std::ranges::replace_copy(std::move(first), std::move(last), std::move(result), d.old_value, 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()); std::same_as> decltype(auto) ret = std::ranges::replace_copy(range, result, d.old_value, 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}, .old_value = 2, .new_value = 5, .expected = {1, 5, 3, 4}}); // empty range test({.input = {}, .old_value = 2, .new_value = 5, .expected = {}}); // all elements match test({.input = {1, 1, 1, 1}, .old_value = 1, .new_value = 2, .expected = {2, 2, 2, 2}}); // no element matches test({.input = {1, 1, 1, 1}, .old_value = 2, .new_value = 3, .expected = {1, 1, 1, 1}}); // old_value and new_value are identical - match test({.input = {1, 1, 1, 1}, .old_value = 1, .new_value = 1, .expected = {1, 1, 1, 1}}); // old_value and new_value are identical - no match test({.input = {1, 1, 1, 1}, .old_value = 2, .new_value = 2, .expected = {1, 1, 1, 1}}); // more elements test( {.input = {1, 2, 3, 4, 5, 6, 7}, .old_value = 2, .new_value = 3, .expected = {1, 3, 3, 4, 5, 6, 7}}); // single element - match test({.input = {1}, .old_value = 1, .new_value = 5, .expected = {5}}); // single element - no match test({.input = {2}, .old_value = 1, .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(std::begin(a), std::end(a), std::begin(b), 1, S{2}, &S::i); assert(ret.in == std::end(a)); assert(ret.out == std::end(b)); } { // range overload S a[] = {{1}, {2}, {3}, {4}}; S b[4]; auto ret = std::ranges::replace_copy(a, std::begin(b), 1, S{2}, &S::i); assert(ret.in == std::end(a)); assert(ret.out == std::end(b)); } } { // Complexity: exactly `last - first` applications of the corresponding predicate and any projection. { // iterator overload int proj_count = 0; int a[] = {1, 2, 3, 4}; int b[4]; std::ranges::replace_copy( std::begin(a), std::end(a), std::begin(b), 0, 0, counting_projection(proj_count)); assert(proj_count == 4); } { // range overload int proj_count = 0; int a[] = {1, 2, 3, 4}; int b[4]; std::ranges::replace_copy(a, std::begin(b), 0, 0, counting_projection(proj_count)); assert(proj_count == 4); } } { // using different types for the old and new values works struct S { constexpr operator int() const { return 0; } constexpr bool operator==(const S&) const = default; constexpr bool operator==(int i) const { return i == 0; } }; struct T { constexpr operator int() const { return 1; } }; { int a[] = {0, 1, 2, 3}; int b[4]; std::ranges::replace_copy(std::begin(a), std::end(a), std::begin(b), S{}, T{}); assert(std::ranges::equal(b, std::array{1, 1, 2, 3})); } { int a[] = {0, 1, 2, 3}; int b[4]; std::ranges::replace_copy(a, std::begin(b), S{}, T{}); assert(std::ranges::equal(b, std::array{1, 1, 2, 3})); } } return true; } int main(int, char**) { test(); static_assert(test()); return 0; }