1 //===----------------------------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // UNSUPPORTED: c++03, c++11, c++14, c++17
13 // template<input_iterator I, sentinel_for<I> S, class T, output_iterator<const T&> O,
14 // class Proj = identity, indirect_unary_predicate<projected<I, Proj>> Pred>
15 // requires indirectly_copyable<I, O>
16 // constexpr replace_copy_if_result<I, O>
17 // replace_copy_if(I first, S last, O result, Pred pred, const T& new_value,
18 // Proj proj = {}); // Since C++20
20 // template<input_range R, class T, output_iterator<const T&> O, class Proj = identity,
21 // indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
22 // requires indirectly_copyable<iterator_t<R>, O>
23 // constexpr replace_copy_if_result<borrowed_iterator_t<R>, O>
24 // replace_copy_if(R&& r, O result, Pred pred, const T& new_value,
25 // Proj proj = {}); // Since C++20
34 #include "almost_satisfies_types.h"
35 #include "counting_predicates.h"
36 #include "counting_projection.h"
37 #include "test_iterators.h"
39 struct FalsePredicate
{
40 constexpr bool operator()(int) { return false; }
43 template <class Iter
, class Sent
= sentinel_wrapper
<Iter
>, class OutIter
= int*>
44 concept HasReplaceCopyIfIter
= requires(Iter
&& first
, Sent
&& last
, OutIter
&& result
) {
45 std::ranges::replace_copy_if(
46 std::forward
<Iter
>(first
), std::forward
<Sent
>(last
), std::forward
<OutIter
>(result
), FalsePredicate
{}, 0);
49 static_assert(HasReplaceCopyIfIter
<int*>);
52 static_assert(!HasReplaceCopyIfIter
<InputIteratorNotDerivedFrom
>);
53 static_assert(!HasReplaceCopyIfIter
<InputIteratorNotIndirectlyReadable
>);
54 static_assert(!HasReplaceCopyIfIter
<InputIteratorNotInputOrOutputIterator
>);
56 // !sentinel_for<S, I>
57 static_assert(!HasReplaceCopyIfIter
<int*, SentinelForNotSemiregular
>);
58 static_assert(!HasReplaceCopyIfIter
<int*, SentinelForNotWeaklyEqualityComparableWith
>);
60 // !output_iterator<O, const T2&>
61 static_assert(!HasReplaceCopyIfIter
<int*, int*, OutputIteratorNotIndirectlyWritable
>);
62 static_assert(!HasReplaceCopyIfIter
<int*, int*, OutputIteratorNotInputOrOutputIterator
>);
64 // !indirect_unary_predicate<Pred, projected<I, Proj>> Pred>
65 static_assert(!HasReplaceCopyIfIter
<IndirectUnaryPredicateNotCopyConstructible
>);
66 static_assert(!HasReplaceCopyIfIter
<IndirectUnaryPredicateNotPredicate
>);
68 // !indirectly_copyable<I, O>
69 static_assert(!HasReplaceCopyIfIter
<int*, int*, int**>);
71 template <class Range
, class OutIter
= int*>
72 concept HasReplaceCopyIfRange
= requires(Range
&& range
, OutIter
&& result
) {
73 std::ranges::replace_copy_if(std::forward
<Range
>(range
), std::forward
<OutIter
>(result
), FalsePredicate
{}, 0);
77 using R
= UncheckedRange
<T
>;
79 static_assert(HasReplaceCopyIfRange
<R
<int*>>);
82 static_assert(!HasReplaceCopyIfRange
<InputRangeNotDerivedFrom
>);
83 static_assert(!HasReplaceCopyIfRange
<InputRangeNotIndirectlyReadable
>);
84 static_assert(!HasReplaceCopyIfRange
<InputRangeNotInputOrOutputIterator
>);
85 static_assert(!HasReplaceCopyIfRange
<InputRangeNotSentinelSemiregular
>);
86 static_assert(!HasReplaceCopyIfRange
<InputRangeNotSentinelEqualityComparableWith
>);
88 // !output_iterator<O, const T2&>
89 static_assert(!HasReplaceCopyIfRange
<R
<int*>, OutputIteratorNotIndirectlyWritable
>);
90 static_assert(!HasReplaceCopyIfRange
<R
<int*>, OutputIteratorNotInputOrOutputIterator
>);
92 // !indirect_unary_predicate<Pred, projected<iterator_t<R>, Proj>> Pred>
93 static_assert(!HasReplaceCopyIfRange
<R
<IndirectUnaryPredicateNotPredicate
>>);
95 // !indirectly_copyable<iterator_t<R>, O>
96 static_assert(!HasReplaceCopyIfRange
<R
<int*>, int**>);
100 std::array
<int, N
> input
;
103 std::array
<int, N
> expected
;
106 template <class InIter
, class Sent
, class OutIter
, int N
>
107 constexpr void test(Data
<N
> d
) {
108 { // iterator overload
109 std::array
<int, N
> output
;
111 auto first
= InIter(d
.input
.data());
112 auto last
= Sent(InIter(d
.input
.data() + d
.input
.size()));
113 auto result
= OutIter(output
.data());
115 auto pred
= [&](int i
) { return i
< d
.cutoff
; };
117 std::same_as
<std::ranges::replace_copy_if_result
<InIter
, OutIter
>> decltype(auto) ret
=
118 std::ranges::replace_copy_if(std::move(first
), std::move(last
), std::move(result
), pred
, d
.new_value
);
119 assert(base(ret
.in
) == d
.input
.data() + d
.input
.size());
120 assert(base(ret
.out
) == output
.data() + output
.size());
121 assert(d
.expected
== output
);
125 std::array
<int, N
> output
;
127 auto range
= std::ranges::subrange(InIter(d
.input
.data()), Sent(InIter(d
.input
.data() + d
.input
.size())));
128 auto result
= OutIter(output
.data());
130 auto pred
= [&](int i
) { return i
< d
.cutoff
; };
132 std::same_as
<std::ranges::replace_copy_if_result
<InIter
, OutIter
>> decltype(auto) ret
=
133 std::ranges::replace_copy_if(range
, result
, pred
, d
.new_value
);
134 assert(base(ret
.in
) == d
.input
.data() + d
.input
.size());
135 assert(base(ret
.out
) == output
.data() + output
.size());
136 assert(d
.expected
== output
);
140 template <class InIter
, class Sent
, class OutIter
>
141 constexpr void tests() {
143 test
<InIter
, Sent
, OutIter
, 4>({.input
= {1, 2, 3, 4}, .cutoff
= 2, .new_value
= 5, .expected
= {5, 2, 3, 4}});
145 test
<InIter
, Sent
, OutIter
, 0>({.input
= {}, .cutoff
= 2, .new_value
= 5, .expected
= {}});
146 // all elements match
147 test
<InIter
, Sent
, OutIter
, 4>({.input
= {1, 1, 1, 1}, .cutoff
= 2, .new_value
= 2, .expected
= {2, 2, 2, 2}});
148 // no element matches
149 test
<InIter
, Sent
, OutIter
, 4>({.input
= {1, 1, 1, 1}, .cutoff
= 1, .new_value
= 3, .expected
= {1, 1, 1, 1}});
151 test
<InIter
, Sent
, OutIter
, 7>(
152 {.input
= {1, 2, 3, 4, 5, 6, 7}, .cutoff
= 3, .new_value
= 3, .expected
= {3, 3, 3, 4, 5, 6, 7}});
153 // single element - match
154 test
<InIter
, Sent
, OutIter
, 1>({.input
= {1}, .cutoff
= 2, .new_value
= 5, .expected
= {5}});
155 // single element - no match
156 test
<InIter
, Sent
, OutIter
, 1>({.input
= {2}, .cutoff
= 2, .new_value
= 5, .expected
= {2}});
159 template <class InIter
, class Sent
>
160 constexpr void test_output_iterators() {
161 tests
<InIter
, Sent
, cpp17_output_iterator
<int*>>();
162 tests
<InIter
, Sent
, forward_iterator
<int*>>();
163 tests
<InIter
, Sent
, bidirectional_iterator
<int*>>();
164 tests
<InIter
, Sent
, random_access_iterator
<int*>>();
165 tests
<InIter
, Sent
, contiguous_iterator
<int*>>();
166 tests
<InIter
, Sent
, int*>();
169 template <class InIter
>
170 constexpr void test_sentinels() {
171 test_output_iterators
<InIter
, InIter
>();
172 test_output_iterators
<InIter
, sentinel_wrapper
<InIter
>>();
173 test_output_iterators
<InIter
, sized_sentinel
<InIter
>>();
176 constexpr bool test() {
177 test_output_iterators
<cpp17_input_iterator
<int*>, sentinel_wrapper
<cpp17_input_iterator
<int*>>>();
178 test_output_iterators
<cpp20_input_iterator
<int*>, sentinel_wrapper
<cpp20_input_iterator
<int*>>>();
179 test_sentinels
<forward_iterator
<int*>>();
180 test_sentinels
<bidirectional_iterator
<int*>>();
181 test_sentinels
<random_access_iterator
<int*>>();
182 test_sentinels
<contiguous_iterator
<int*>>();
183 test_sentinels
<int*>();
184 test_sentinels
<const int*>();
186 { // check that a custom projection works
191 { // iterator overload
192 S a
[] = {{1}, {2}, {3}, {4}};
194 auto ret
= std::ranges::replace_copy_if(std::begin(a
), std::end(a
), std::begin(b
), FalsePredicate
{}, S
{2}, &S::i
);
195 assert(ret
.in
== std::end(a
));
196 assert(ret
.out
== std::end(b
));
197 assert(std::ranges::equal(a
, b
, {}, &S::i
, &S::i
));
201 S a
[] = {{1}, {2}, {3}, {4}};
203 auto ret
= std::ranges::replace_copy_if(a
, std::begin(b
), FalsePredicate
{}, S
{2}, &S::i
);
204 assert(ret
.in
== std::end(a
));
205 assert(ret
.out
== std::end(b
));
206 assert(std::ranges::equal(a
, b
, {}, &S::i
, &S::i
));
210 { // Complexity: exactly `last - first` applications of the corresponding predicate and any projection.
211 { // iterator overload
214 int a
[] = {1, 2, 3, 4};
217 std::ranges::replace_copy_if(
218 std::begin(a
), std::end(a
), std::begin(b
),
219 counting_predicate(FalsePredicate
{}, pred_count
), 0, counting_projection(proj_count
));
220 assert(pred_count
== 4);
221 assert(proj_count
== 4);
227 int a
[] = {1, 2, 3, 4};
230 std::ranges::replace_copy_if(a
, std::begin(b
),
231 counting_predicate(FalsePredicate
{}, pred_count
), 0, counting_projection(proj_count
));
232 assert(pred_count
== 4);
233 assert(proj_count
== 4);
237 { // using different types for the old and new values works
239 constexpr operator int() const { return 1; }
242 int a
[] = {0, 0, 2, 3};
244 std::ranges::replace_copy_if(std::begin(a
), std::end(a
), std::begin(b
), [](int i
) { return i
< 2; }, S
{});
245 assert(std::ranges::equal(b
, std::array
{1, 1, 2, 3}));
249 int a
[] = {0, 0, 2, 3};
251 std::ranges::replace_copy_if(a
, std::begin(b
), [](int i
) { return i
< 2; }, S
{});
252 assert(std::ranges::equal(b
, std::array
{1, 1, 2, 3}));
259 int main(int, char**) {
261 static_assert(test());