diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv --- a/libcxx/docs/Status/RangesAlgorithms.csv +++ b/libcxx/docs/Status/RangesAlgorithms.csv @@ -47,8 +47,8 @@ Write,transform,Nikolas Klauser,`D122173 `_,✅ Write,generate,Konstantin Varlamov,`D130552 `_,✅ Write,generate_n,Konstantin Varlamov,`D130552 `_,✅ -Write,remove_copy,Nikolas Klauser,n/a,Not started -Write,remove_copy_if,Nikolas Klauser,n/a,Not started +Write,remove_copy,Nikolas Klauser,`D130599 `_,✅ +Write,remove_copy_if,Nikolas Klauser,`D130599 `_,✅ Write,replace,Nikolas Klauser,`D126283 `_,✅ Write,replace_if,Nikolas Klauser,`D126283 `_,✅ Write,replace_copy,Nikolas Klauser,`D129806 `_,Under review diff --git a/libcxx/include/__algorithm/ranges_remove_copy.h b/libcxx/include/__algorithm/ranges_remove_copy.h --- a/libcxx/include/__algorithm/ranges_remove_copy.h +++ b/libcxx/include/__algorithm/ranges_remove_copy.h @@ -10,19 +10,16 @@ #define _LIBCPP___ALGORITHM_RANGES_REMOVE_COPY_H #include <__algorithm/in_out_result.h> -#include <__algorithm/make_projected.h> -#include <__algorithm/remove_copy.h> +#include <__algorithm/ranges_remove_copy_if.h> #include <__config> #include <__functional/identity.h> #include <__functional/invoke.h> #include <__functional/ranges_operations.h> #include <__iterator/concepts.h> -#include <__iterator/iterator_traits.h> #include <__iterator/projected.h> #include <__ranges/access.h> #include <__ranges/concepts.h> #include <__ranges/dangling.h> -#include <__utility/forward.h> #include <__utility/move.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -40,32 +37,30 @@ namespace __remove_copy { -struct __fn { - - template _Sent, weakly_incrementable _OutIter, class _Type, - class _Proj = identity> - requires indirectly_copyable<_InIter, _OutIter> && - indirect_binary_predicate, const _Type*> - _LIBCPP_HIDE_FROM_ABI constexpr - remove_copy_result<_InIter, _OutIter> - operator()(_InIter __first, _Sent __last, _OutIter __result, const _Type& __value, _Proj __proj = {}) const { - // TODO: implement - (void)__first; (void)__last; (void)__result; (void)__value; (void)__proj; - return {}; - } - - template - requires indirectly_copyable, _OutIter> && - indirect_binary_predicate, _Proj>, const _Type*> - _LIBCPP_HIDE_FROM_ABI constexpr - remove_copy_result, _OutIter> - operator()(_Range&& __range, _OutIter __result, const _Type& __value, _Proj __proj = {}) const { - // TODO: implement - (void)__range; (void)__result; (void)__value; (void)__proj; - return {}; - } - -}; + struct __fn { + template _Sent, + weakly_incrementable _OutIter, + class _Type, + class _Proj = identity> + requires indirectly_copyable<_InIter, _OutIter> && + indirect_binary_predicate, const _Type*> + _LIBCPP_HIDE_FROM_ABI constexpr remove_copy_result<_InIter, _OutIter> + operator()(_InIter __first, _Sent __last, _OutIter __result, const _Type& __value, _Proj __proj = {}) const { + auto __pred = [&](auto&& __val) { return __value == __val; }; + return ranges::__remove_copy_if_impl(std::move(__first), std::move(__last), std::move(__result), __pred, __proj); + } + + template + requires indirectly_copyable, _OutIter> && + indirect_binary_predicate, _Proj>, const _Type*> + _LIBCPP_HIDE_FROM_ABI constexpr remove_copy_result, _OutIter> + operator()(_Range&& __range, _OutIter __result, const _Type& __value, _Proj __proj = {}) const { + auto __pred = [&](auto&& __val) { return __value == __val; }; + return ranges::__remove_copy_if_impl( + ranges::begin(__range), ranges::end(__range), std::move(__result), __pred, __proj); + } + }; } // namespace __remove_copy diff --git a/libcxx/include/__algorithm/ranges_remove_copy_if.h b/libcxx/include/__algorithm/ranges_remove_copy_if.h --- a/libcxx/include/__algorithm/ranges_remove_copy_if.h +++ b/libcxx/include/__algorithm/ranges_remove_copy_if.h @@ -38,33 +38,43 @@ template using remove_copy_if_result = in_out_result<_InIter, _OutIter>; -namespace __remove_copy_if { - -struct __fn { - - template _Sent, weakly_incrementable _OutIter, - class _Proj = identity, indirect_unary_predicate> _Pred> - requires indirectly_copyable<_InIter, _OutIter> - _LIBCPP_HIDE_FROM_ABI constexpr - remove_copy_if_result<_InIter, _OutIter> - operator()(_InIter __first, _Sent __last, _OutIter __result, _Pred __pred, _Proj __proj = {}) const { - // TODO: implement - (void)__first; (void)__last; (void)__result; (void)__pred; (void)__proj; - return {}; +template +_LIBCPP_HIDE_FROM_ABI constexpr in_out_result<_InIter, _OutIter> +__remove_copy_if_impl(_InIter __first, _Sent __last, _OutIter __result, _Pred& __pred, _Proj& __proj) { + for (; __first != __last; ++__first) { + if (!std::invoke(__pred, std::invoke(__proj, *__first))) { + *__result = *__first; + ++__result; + } } + return {std::move(__first), std::move(__result)}; +} - template , _Proj>> _Pred> - requires indirectly_copyable, _OutIter> - _LIBCPP_HIDE_FROM_ABI constexpr - remove_copy_if_result, _OutIter> - operator()(_Range&& __range, _OutIter __result, _Pred __pred, _Proj __proj = {}) const { - // TODO: implement - (void)__range; (void)__result; (void)__pred; (void)__proj; - return {}; - } +namespace __remove_copy_if { -}; + struct __fn { + template _Sent, + weakly_incrementable _OutIter, + class _Proj = identity, + indirect_unary_predicate> _Pred> + requires indirectly_copyable<_InIter, _OutIter> + _LIBCPP_HIDE_FROM_ABI constexpr remove_copy_if_result<_InIter, _OutIter> + operator()(_InIter __first, _Sent __last, _OutIter __result, _Pred __pred, _Proj __proj = {}) const { + return ranges::__remove_copy_if_impl(std::move(__first), std::move(__last), std::move(__result), __pred, __proj); + } + + template , _Proj>> _Pred> + requires indirectly_copyable, _OutIter> + _LIBCPP_HIDE_FROM_ABI constexpr remove_copy_if_result, _OutIter> + operator()(_Range&& __range, _OutIter __result, _Pred __pred, _Proj __proj = {}) const { + return ranges::__remove_copy_if_impl( + ranges::begin(__range), ranges::end(__range), std::move(__result), __pred, __proj); + } + }; } // namespace __remove_copy_if diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -912,6 +912,38 @@ indirectly_copyable_storable, O>) constexpr unique_copy_result, O> unique_copy(R&& r, O result, C comp = {}, Proj proj = {}); // Since C++20 + + template + using remove_copy_result = in_out_result; // Since C++20 + + template S, weakly_incrementable O, class T, + class Proj = identity> + 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 + + template + using remove_copy_if_result = in_out_result; // Since C++20 + + template S, weakly_incrementable O, + class Proj = identity, indirect_unary_predicate> Pred> + requires indirectly_copyable + constexpr remove_copy_if_result + remove_copy_if(I first, S last, O result, Pred pred, Proj proj = {}); // Since C++20 + + template, Proj>> Pred> + requires indirectly_copyable, O> + constexpr remove_copy_if_result, O> + remove_copy_if(R&& r, O result, Pred pred, Proj proj = {}); // Since C++20 + } constexpr bool // constexpr in C++20 @@ -1694,6 +1726,8 @@ #include <__algorithm/ranges_pop_heap.h> #include <__algorithm/ranges_push_heap.h> #include <__algorithm/ranges_remove.h> +#include <__algorithm/ranges_remove_copy.h> +#include <__algorithm/ranges_remove_copy_if.h> #include <__algorithm/ranges_remove_if.h> #include <__algorithm/ranges_replace.h> #include <__algorithm/ranges_replace_if.h> diff --git a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp --- a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp +++ b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp @@ -187,8 +187,8 @@ //(void)std::ranges::prev_permutation(a, Less(&copies)); assert(copies == 0); (void)std::ranges::push_heap(first, last, Less(&copies)); assert(copies == 0); (void)std::ranges::push_heap(a, Less(&copies)); assert(copies == 0); - //(void)std::ranges::remove_copy_if(first, last, first2, UnaryTrue(&copies)); assert(copies == 0); - //(void)std::ranges::remove_copy_if(a, first2, UnaryTrue(&copies)); assert(copies == 0); + (void)std::ranges::remove_copy_if(first, last, first2, UnaryTrue(&copies)); assert(copies == 0); + (void)std::ranges::remove_copy_if(a, first2, UnaryTrue(&copies)); assert(copies == 0); (void)std::ranges::remove_if(first, last, UnaryTrue(&copies)); assert(copies == 0); (void)std::ranges::remove_if(a, UnaryTrue(&copies)); assert(copies == 0); //(void)std::ranges::replace_copy_if(first, last, first2, UnaryTrue(&copies), value); assert(copies == 0); diff --git a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp --- a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp +++ b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp @@ -170,10 +170,10 @@ //(void)std::ranges::prev_permutation(a, Less(), Proj(&copies)); assert(copies == 0); (void)std::ranges::push_heap(first, last, Less(), Proj(&copies)); assert(copies == 0); (void)std::ranges::push_heap(a, Less(), Proj(&copies)); assert(copies == 0); - //(void)std::ranges::remove_copy(first, last, first2, value, Proj(&copies)); assert(copies == 0); - //(void)std::ranges::remove_copy(a, first2, value, Proj(&copies)); assert(copies == 0); - //(void)std::ranges::remove_copy_if(first, last, first2, UnaryTrue(), Proj(&copies)); assert(copies == 0); - //(void)std::ranges::remove_copy_if(a, first2, UnaryTrue(), Proj(&copies)); assert(copies == 0); + (void)std::ranges::remove_copy(first, last, first2, value, Proj(&copies)); assert(copies == 0); + (void)std::ranges::remove_copy(a, first2, value, Proj(&copies)); assert(copies == 0); + (void)std::ranges::remove_copy_if(first, last, first2, UnaryTrue(), Proj(&copies)); assert(copies == 0); + (void)std::ranges::remove_copy_if(a, first2, UnaryTrue(), Proj(&copies)); assert(copies == 0); (void)std::ranges::remove(first, last, value, Proj(&copies)); assert(copies == 0); (void)std::ranges::remove(a, value, Proj(&copies)); assert(copies == 0); (void)std::ranges::remove_if(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0); diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges_remove_copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges_remove_copy.pass.cpp --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges_remove_copy.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges_remove_copy.pass.cpp @@ -30,16 +30,223 @@ #include #include #include +#include #include "almost_satisfies_types.h" +#include "counting_projection.h" #include "test_iterators.h" -// TODO: SFINAE tests. +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() { - // TODO: main tests. - // TODO: A custom comparator works. - // TODO: A custom projection works. + 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; } diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges_remove_copy_if.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges_remove_copy_if.pass.cpp --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges_remove_copy_if.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges_remove_copy_if.pass.cpp @@ -28,16 +28,264 @@ #include #include #include +#include +#include #include "almost_satisfies_types.h" +#include "counting_predicates.h" +#include "counting_projection.h" #include "test_iterators.h" -// TODO: SFINAE tests. +struct AlwaysTrue { + constexpr bool operator()(auto&&...) const { return true; } +}; + +template < + class I, + class S = sentinel_wrapper>, + class O = int*, + class Pred = AlwaysTrue> +concept HasRemoveCopyIfIter = + requires(I&& iter, S&& sent, O&& out, Pred&& pred) { + std::ranges::remove_copy_if(std::forward(iter), std::forward(sent), + std::forward(out), std::forward(pred)); +}; + +static_assert(HasRemoveCopyIfIter); + +// !input_iterator +static_assert(!HasRemoveCopyIfIter); +static_assert(!HasRemoveCopyIfIter>); + +// !sentinel_for +static_assert(!HasRemoveCopyIfIter); +static_assert(!HasRemoveCopyIfIter); + +// !weakly_incrementable +static_assert(!HasRemoveCopyIfIter); + +// !indirect_unary_predicate> +static_assert(!HasRemoveCopyIfIter); +static_assert(!HasRemoveCopyIfIter); + +// !indirectly_copyable +static_assert(!HasRemoveCopyIfIter); +static_assert(!HasRemoveCopyIfIter); + +template < class R, class O = int*, class Pred = AlwaysTrue, class Proj = std::identity> +concept HasRemoveCopyIfRange = + requires(R&& r, O&& out, Pred&& pred, Proj&& proj) { + std::ranges::remove_copy_if( + std::forward(r), std::forward(out), std::forward(pred), std::forward(proj)); + }; + +template +using R = UncheckedRange; + +static_assert(HasRemoveCopyIfRange>); + +// !input_range +static_assert(!HasRemoveCopyIfRange>); +static_assert(!HasRemoveCopyIfRange>>); + +// !weakly_incrementable +static_assert(!HasRemoveCopyIfRange, WeaklyIncrementableNotMovable>); + +// !indirect_unary_predicate, Proj>> +static_assert(!HasRemoveCopyIfRange, int*, IndirectUnaryPredicateNotPredicate>); +static_assert(!HasRemoveCopyIfRange, int*, IndirectUnaryPredicateNotCopyConstructible>); + +// !indirectly_copyable, O> +static_assert(!HasRemoveCopyIfRange, OutputIteratorNotIndirectlyWritable>); +static_assert(!HasRemoveCopyIfRange, const int*>); + +template class SentWrapper, std::size_t N1, std::size_t N2, class Pred> +constexpr void testRemoveCopyIfImpl(std::array in, std::array expected, Pred pred) { + using Sent = SentWrapper; + using Result = std::ranges::remove_copy_if_result; + + // iterator overload + { + std::array out; + std::same_as decltype(auto) result = + std::ranges::remove_copy_if(InIter{in.data()}, Sent{InIter{in.data() + in.size()}}, OutIter{out.data()}, pred); + assert(std::ranges::equal(out, expected)); + assert(base(result.in) == in.data() + in.size()); + assert(base(result.out) == out.data() + out.size()); + } + + // range overload + { + std::array out; + std::ranges::subrange r{InIter{in.data()}, Sent{InIter{in.data() + in.size()}}}; + std::same_as decltype(auto) result = + std::ranges::remove_copy_if(r, OutIter{out.data()}, pred); + assert(std::ranges::equal(out, expected)); + assert(base(result.in) == in.data() + in.size()); + assert(base(result.out) == out.data() + out.size()); + } +} + +template class SentWrapper> +constexpr void testImpl() { + // remove multiple elements + { + std::array in{1, 2, 3, 2, 1}; + std::array expected{1, 3, 1}; + auto pred = [](int i) { return i == 2; }; + testRemoveCopyIfImpl(in, expected, pred); + } + + // remove single elements + { + std::array in{1, 2, 3, 2, 1}; + std::array expected{1, 2, 2, 1}; + auto pred = [](int i) { return i == 3; }; + testRemoveCopyIfImpl(in, expected, pred); + } + + // nothing removed + { + std::array in{1, 2, 3, 2, 1}; + std::array expected{1, 2, 3, 2, 1}; + auto pred = [](int) { return false; }; + testRemoveCopyIfImpl(in, expected, pred); + } + + // all removed + { + std::array in{1, 2, 3, 2, 1}; + std::array expected{}; + auto pred = [](int) { return true; }; + testRemoveCopyIfImpl(in, expected, pred); + } + + // remove first + { + std::array in{1, 2, 3, 2}; + std::array expected{2, 3, 2}; + auto pred = [](int i) { return i < 2; }; + testRemoveCopyIfImpl(in, expected, pred); + } + + // remove last + { + std::array in{1, 2, 3, 2, 5}; + std::array expected{1, 2, 3, 2}; + auto pred = [](int i) { return i > 3; }; + testRemoveCopyIfImpl(in, expected, pred); + } + + // stable + { + std::array in{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + std::array expected{6, 7, 8, 9, 10}; + auto pred = [](int i) { return i < 6; }; + testRemoveCopyIfImpl(in, expected, pred); + } + + // empty range + { + std::array in{}; + std::array expected{}; + auto pred = [](int) { return false; }; + testRemoveCopyIfImpl(in, expected, pred); + } + + // one element range + { + std::array in{1}; + std::array expected{}; + auto pred = [](int i) { return i == 1; }; + testRemoveCopyIfImpl(in, expected, pred); + } +} + +template class SentWrapper> +constexpr void withAllPermutationsOfInIter() { + testImpl, OutIter, sentinel_wrapper>(); + testImpl, OutIter, SentWrapper>(); + testImpl, OutIter, SentWrapper>(); + testImpl, OutIter, SentWrapper>(); + testImpl, OutIter, SentWrapper>(); + testImpl(); +} + +template