diff --git a/libcxx/include/__algorithm/for_each.h b/libcxx/include/__algorithm/for_each.h index cb26aa4d2656a..85fedce3d936d 100644 --- a/libcxx/include/__algorithm/for_each.h +++ b/libcxx/include/__algorithm/for_each.h @@ -11,6 +11,7 @@ #define _LIBCPP___ALGORITHM_FOR_EACH_H #include <__algorithm/for_each_segment.h> +#include <__algorithm/specialized_algorithms.h> #include <__config> #include <__functional/identity.h> #include <__iterator/segmented_iterator.h> @@ -27,7 +28,12 @@ template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator __for_each(_InputIterator __first, _Sent __last, _Func& __func, _Proj& __proj) { #ifndef _LIBCPP_CXX03_LANG - if constexpr (is_same<_InputIterator, _Sent>::value && __is_segmented_iterator_v<_InputIterator>) { + if constexpr (using _SpecialAlg = + __specialized_algorithm<_Algorithm::__for_each, __iterator_pair<_InputIterator, _Sent>>; + _SpecialAlg::__has_algorithm) { + _SpecialAlg()(__first, __last, __func, __proj); + return __last; + } else if constexpr (is_same<_InputIterator, _Sent>::value && __is_segmented_iterator_v<_InputIterator>) { using __local_iterator_t = typename __segmented_iterator_traits<_InputIterator>::__local_iterator; std::__for_each_segment(__first, __last, [&](__local_iterator_t __lfirst, __local_iterator_t __llast) { std::__for_each(__lfirst, __llast, __func, __proj); diff --git a/libcxx/include/__algorithm/ranges_for_each.h b/libcxx/include/__algorithm/ranges_for_each.h index e9c84e8583f87..7a547fb269b4b 100644 --- a/libcxx/include/__algorithm/ranges_for_each.h +++ b/libcxx/include/__algorithm/ranges_for_each.h @@ -12,6 +12,7 @@ #include <__algorithm/for_each.h> #include <__algorithm/for_each_n.h> #include <__algorithm/in_fun_result.h> +#include <__algorithm/specialized_algorithms.h> #include <__concepts/assignable.h> #include <__config> #include <__functional/identity.h> @@ -20,6 +21,7 @@ #include <__ranges/access.h> #include <__ranges/concepts.h> #include <__ranges/dangling.h> +#include <__type_traits/remove_cvref.h> #include <__utility/move.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -71,7 +73,13 @@ struct __for_each { indirectly_unary_invocable, _Proj>> _Func> _LIBCPP_HIDE_FROM_ABI constexpr for_each_result, _Func> operator()(_Range&& __range, _Func __func, _Proj __proj = {}) const { - return __for_each_impl(ranges::begin(__range), ranges::end(__range), __func, __proj); + using _SpecialAlg = __specialized_algorithm<_Algorithm::__for_each, __single_range>>; + if constexpr (_SpecialAlg::__has_algorithm) { + auto [__iter, __func2] = _SpecialAlg()(__range, std::move(__func), std::move(__proj)); + return {std::move(__iter), std::move(__func)}; + } else { + return __for_each_impl(ranges::begin(__range), ranges::end(__range), __func, __proj); + } } }; diff --git a/libcxx/include/__algorithm/specialized_algorithms.h b/libcxx/include/__algorithm/specialized_algorithms.h index a2ffd36f0c87d..1d3bc8723c3f3 100644 --- a/libcxx/include/__algorithm/specialized_algorithms.h +++ b/libcxx/include/__algorithm/specialized_algorithms.h @@ -19,11 +19,18 @@ _LIBCPP_BEGIN_NAMESPACE_STD namespace _Algorithm { struct __fill_n {}; +struct __for_each {}; } // namespace _Algorithm template struct __single_iterator; +template +struct __iterator_pair; + +template +struct __single_range; + // This struct allows specializing algorithms for specific arguments. This is useful when we know a more efficient // algorithm implementation for e.g. library-defined iterators. _Alg is one of tags defined inside the _Algorithm // namespace above. _Ranges is an essentially arbitrary subset of the arguments to the algorithm that are used for diff --git a/libcxx/include/__tree b/libcxx/include/__tree index ceae22bb48702..5559862e2a1c6 100644 --- a/libcxx/include/__tree +++ b/libcxx/include/__tree @@ -11,6 +11,7 @@ #define _LIBCPP___TREE #include <__algorithm/min.h> +#include <__algorithm/specialized_algorithms.h> #include <__assert> #include <__config> #include <__fwd/pair.h> @@ -36,6 +37,7 @@ #include <__type_traits/is_swappable.h> #include <__type_traits/make_transparent.h> #include <__type_traits/remove_const.h> +#include <__type_traits/remove_cvref.h> #include <__utility/forward.h> #include <__utility/lazy_synth_three_way_comparator.h> #include <__utility/move.h> @@ -656,6 +658,54 @@ struct __generic_container_node_destructor<__tree_node<_Tp, _VoidPtr>, _Alloc> : }; #endif +// Do an in-order traversal of the tree until `__break` returns true. Takes the root node of the tree. +template +_LIBCPP_HIDE_FROM_ABI bool __tree_iterate_from_root(_Break __break, _NodePtr __root, _Func& __func, _Proj& __proj) { + if (__root->__left_) { + if (std::__tree_iterate_from_root<_Reference>(__break, static_cast<_NodePtr>(__root->__left_), __func, __proj)) + return true; + } + if (__break(__root)) + return true; + __func(static_cast<_Reference>(__root->__get_value())); + if (__root->__right_) + return std::__tree_iterate_from_root<_Reference>(__break, static_cast<_NodePtr>(__root->__right_), __func, __proj); + return false; +} + +// Do an in-order traversal of the tree from __first to __last. +template +_LIBCPP_HIDE_FROM_ABI void +__tree_iterate_subrange(_NodeIter __first_it, _NodeIter __last_it, _Func& __func, _Proj& __proj) { + using _NodePtr = _NodeIter::__node_pointer; + using _Reference = _NodeIter::reference; + + auto __first = __first_it.__ptr_; + auto __last = __last_it.__ptr_; + + while (true) { + if (__first == __last) + return; + auto __nfirst = static_cast<_NodePtr>(__first); + __func(static_cast<_Reference>(__nfirst->__get_value())); + if (__nfirst->__right_) { + if (std::__tree_iterate_from_root<_Reference>( + [&](_NodePtr __node) -> bool { return __node == __last; }, + static_cast<_NodePtr>(__nfirst->__right_), + __func, + __proj)) + return; + } + if (std::__tree_is_left_child(__nfirst)) { + __first = __nfirst->__parent_; + } else { + do { + __first = __nfirst->__parent_; + } while (!std::__tree_is_left_child(__nfirst)); + } + } +} + template class __tree_iterator { using _NodeTypes _LIBCPP_NODEBUG = __tree_node_types<_NodePtr>; @@ -715,8 +765,27 @@ private: friend class __tree; template friend class __tree_const_iterator; + + template + friend void __tree_iterate_subrange(_NodeIter, _NodeIter, _Func&, _Proj&); }; +#ifndef _LIBCPP_CXX03_LANG +template +struct __specialized_algorithm< + _Algorithm::__for_each, + __iterator_pair<__tree_iterator<_Tp, _NodePtr, _DiffType>, __tree_iterator<_Tp, _NodePtr, _DiffType>>> { + static const bool __has_algorithm = true; + + using __iterator _LIBCPP_NODEBUG = __tree_iterator<_Tp, _NodePtr, _DiffType>; + + template + _LIBCPP_HIDE_FROM_ABI static void operator()(__iterator __first, __iterator __last, _Func& __func, _Proj& __proj) { + std::__tree_iterate_subrange(__first, __last, __func, __proj); + } +}; +#endif + template class __tree_const_iterator { using _NodeTypes _LIBCPP_NODEBUG = __tree_node_types<_NodePtr>; @@ -780,8 +849,27 @@ private: template friend class __tree; + + template + friend void __tree_iterate_subrange(_NodeIter, _NodeIter, _Func&, _Proj&); }; +#ifndef _LIBCPP_CXX03_LANG +template +struct __specialized_algorithm< + _Algorithm::__for_each, + __iterator_pair<__tree_const_iterator<_Tp, _NodePtr, _DiffType>, __tree_const_iterator<_Tp, _NodePtr, _DiffType>>> { + static const bool __has_algorithm = true; + + using __iterator _LIBCPP_NODEBUG = __tree_const_iterator<_Tp, _NodePtr, _DiffType>; + + template + _LIBCPP_HIDE_FROM_ABI static void operator()(__iterator __first, __iterator __last, _Func& __func, _Proj& __proj) { + std::__tree_iterate_subrange(__first, __last, __func, __proj); + } +}; +#endif + template #ifndef _LIBCPP_CXX03_LANG _LIBCPP_DIAGNOSE_WARNING(!__is_invocable_v<_Compare const&, _Tp const&, _Tp const&>, @@ -1484,7 +1572,25 @@ private: [](value_type& __lhs, value_type& __rhs) { __assign_value(__lhs, std::move(__rhs)); }, [this](__node_pointer __nd) { return __move_construct_tree(__nd); }); } + + friend struct __specialized_algorithm<_Algorithm::__for_each, __single_range<__tree> >; +}; + +#if _LIBCPP_STD_VER >= 14 +template +struct __specialized_algorithm<_Algorithm::__for_each, __single_range<__tree<_Tp, _Compare, _Allocator> > > { + static const bool __has_algorithm = true; + + using __node_pointer _LIBCPP_NODEBUG = typename __tree<_Tp, _Compare, _Allocator>::__node_pointer; + + template + _LIBCPP_HIDE_FROM_ABI static auto operator()(_Tree&& __range, _Func __func, _Proj __proj) { + std::__tree_iterate_from_root<__copy_cvref_t<_Tree, typename __remove_cvref_t<_Tree>::value_type>>( + [](__node_pointer) { return false; }, __range.__root(), __func, __proj); + return std::make_pair(__range.end(), std::move(__func)); + } }; +#endif // Precondition: __size_ != 0 template diff --git a/libcxx/include/map b/libcxx/include/map index 0dca11cabd12e..7845cbcb85691 100644 --- a/libcxx/include/map +++ b/libcxx/include/map @@ -577,6 +577,7 @@ erase_if(multimap& c, Predicate pred); // C++20 # include <__algorithm/equal.h> # include <__algorithm/lexicographical_compare.h> # include <__algorithm/lexicographical_compare_three_way.h> +# include <__algorithm/specialized_algorithms.h> # include <__assert> # include <__config> # include <__functional/binary_function.h> @@ -1370,6 +1371,8 @@ private: # ifdef _LIBCPP_CXX03_LANG _LIBCPP_HIDE_FROM_ABI __node_holder __construct_node_with_key(const key_type& __k); # endif + + friend struct __specialized_algorithm<_Algorithm::__for_each, __single_range >; }; # if _LIBCPP_STD_VER >= 17 @@ -1422,6 +1425,22 @@ map(initializer_list>, _Allocator) -> map, _Tp, less>, _Allocator>; # endif +# if _LIBCPP_STD_VER >= 14 +template +struct __specialized_algorithm<_Algorithm::__for_each, __single_range>> { + using __map _LIBCPP_NODEBUG = map<_Key, _Tp, _Compare, _Allocator>; + + static const bool __has_algorithm = true; + + template + _LIBCPP_HIDE_FROM_ABI static auto operator()(_Map&& __map, _Func __func, _Proj __proj) { + auto [_, __func2] = __specialized_algorithm<_Algorithm::__for_each, __single_range>()( + __map.__tree_, std::move(__func), std::move(__proj)); + return std::make_pair(__map.end(), std::move(__func2)); + } +}; +# endif + # ifndef _LIBCPP_CXX03_LANG template _Tp& map<_Key, _Tp, _Compare, _Allocator>::operator[](const key_type& __k) { @@ -1920,6 +1939,8 @@ private: typedef __map_node_destructor<__node_allocator> _Dp; typedef unique_ptr<__node, _Dp> __node_holder; + + friend struct __specialized_algorithm<_Algorithm::__for_each, __single_range >; }; # if _LIBCPP_STD_VER >= 17 @@ -1972,6 +1993,22 @@ multimap(initializer_list>, _Allocator) -> multimap, _Tp, less>, _Allocator>; # endif +# if _LIBCPP_STD_VER >= 14 +template +struct __specialized_algorithm<_Algorithm::__for_each, __single_range>> { + using __map _LIBCPP_NODEBUG = multimap<_Key, _Tp, _Compare, _Allocator>; + + static const bool __has_algorithm = true; + + template + _LIBCPP_HIDE_FROM_ABI static auto operator()(_Map&& __map, _Func __func, _Proj __proj) { + auto [_, __func2] = __specialized_algorithm<_Algorithm::__for_each, __single_range>()( + __map.__tree_, std::move(__func), std::move(__proj)); + return std::make_pair(__map.end(), std::move(__func2)); + } +}; +# endif + template inline _LIBCPP_HIDE_FROM_ABI bool operator==(const multimap<_Key, _Tp, _Compare, _Allocator>& __x, const multimap<_Key, _Tp, _Compare, _Allocator>& __y) { diff --git a/libcxx/include/set b/libcxx/include/set index 3d6f571a42a1a..bd6e139f6e075 100644 --- a/libcxx/include/set +++ b/libcxx/include/set @@ -518,6 +518,7 @@ erase_if(multiset& c, Predicate pred); // C++20 # include <__algorithm/equal.h> # include <__algorithm/lexicographical_compare.h> # include <__algorithm/lexicographical_compare_three_way.h> +# include <__algorithm/specialized_algorithms.h> # include <__assert> # include <__config> # include <__functional/is_transparent.h> @@ -898,6 +899,9 @@ public: return __tree_.__equal_range_multi(__k); } # endif + + template + friend struct __specialized_algorithm; }; # if _LIBCPP_STD_VER >= 17 @@ -944,6 +948,23 @@ template , _Allocator) -> set<_Key, less<_Key>, _Allocator>; # endif +# if _LIBCPP_STD_VER >= 14 +template +struct __specialized_algorithm<_Alg, __single_range>> { + using __set _LIBCPP_NODEBUG = set<_Key, _Compare, _Allocator>; + + static const bool __has_algorithm = + __specialized_algorithm<_Alg, __single_range>::__has_algorithm; + + // set's begin() and end() are identical with and without const qualification + template + _LIBCPP_HIDE_FROM_ABI static auto operator()(const __set& __set, _Args&&... __args) { + return __specialized_algorithm<_Alg, __single_range>()( + __set.__tree_, std::forward<_Args>(__args)...); + } +}; +# endif + template inline _LIBCPP_HIDE_FROM_ABI bool operator==(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) { @@ -1342,6 +1363,9 @@ public: return __tree_.__equal_range_multi(__k); } # endif + + template + friend struct __specialized_algorithm; }; # if _LIBCPP_STD_VER >= 17 @@ -1389,6 +1413,23 @@ template , _Allocator) -> multiset<_Key, less<_Key>, _Allocator>; # endif +# if _LIBCPP_STD_VER >= 14 +template +struct __specialized_algorithm<_Alg, __single_range>> { + using __set _LIBCPP_NODEBUG = multiset<_Key, _Compare, _Allocator>; + + static const bool __has_algorithm = + __specialized_algorithm<_Alg, __single_range>::__has_algorithm; + + // set's begin() and end() are identical with and without const qualification + template + _LIBCPP_HIDE_FROM_ABI static auto operator()(const __set& __set, _Args&&... __args) { + return __specialized_algorithm<_Alg, __single_range>()( + __set.__tree_, std::forward<_Args>(__args)...); + } +}; +# endif + template inline _LIBCPP_HIDE_FROM_ABI bool operator==(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) { diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp index f58f336f8b892..f2c19cbe1b897 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp @@ -12,8 +12,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -23,7 +25,7 @@ int main(int argc, char** argv) { // {std,ranges}::for_each { - auto bm = [](std::string name, auto for_each) { + auto sequence_bm = [](std::string name, auto for_each) { using ElemType = typename Container::value_type; benchmark::RegisterBenchmark( name, @@ -44,12 +46,98 @@ int main(int argc, char** argv) { ->Arg(50) // non power-of-two ->Arg(8192); }; - bm.operator()>("std::for_each(vector)", std_for_each); - bm.operator()>("std::for_each(deque)", std_for_each); - bm.operator()>("std::for_each(list)", std_for_each); - bm.operator()>("rng::for_each(vector)", std::ranges::for_each); - bm.operator()>("rng::for_each(deque)", std::ranges::for_each); - bm.operator()>("rng::for_each(list)", std::ranges::for_each); + sequence_bm.operator()>("std::for_each(vector)", std_for_each); + sequence_bm.operator()>("std::for_each(deque)", std_for_each); + sequence_bm.operator()>("std::for_each(list)", std_for_each); + sequence_bm.operator()>("rng::for_each(vector)", std::ranges::for_each); + sequence_bm.operator()>("rng::for_each(deque)", std::ranges::for_each); + sequence_bm.operator()>("rng::for_each(list)", std::ranges::for_each); + + auto associative_bm = + []( + std::type_identity, std::bool_constant, std::string name, auto for_each) { + benchmark::RegisterBenchmark( + name, + [for_each](auto& st) { + Container c; + for (size_t i = 0; i != st.range(0); ++i) { + if constexpr (IsMapLike) + c.emplace(i); + else + c.emplace(i, i); + } + + for (auto _ : st) { + benchmark::DoNotOptimize(c); + for_each(c.begin(), c.end(), [](auto v) { benchmark::DoNotOptimize(&v); }); + } + }) + ->Arg(8) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(8192); + }; + associative_bm(std::type_identity>{}, + std::false_type{}, + "rng::for_each(set::iterator)", + std::ranges::for_each); + associative_bm(std::type_identity>{}, + std::false_type{}, + "rng::for_each(multiset::iterator)", + std::ranges::for_each); + associative_bm(std::type_identity>{}, + std::true_type{}, + "rng::for_each(map::iterator)", + std::ranges::for_each); + associative_bm(std::type_identity>{}, + std::true_type{}, + "rng::for_each(multimap::iterator)", + std::ranges::for_each); + + auto associative_ranges_bm = + []( + std::type_identity, std::bool_constant, std::string name, auto for_each) { + benchmark::RegisterBenchmark( + name, + [for_each](auto& st) { + Container c; + for (size_t i = 0; i != st.range(0); ++i) { + if constexpr (IsMapLike) + c.emplace(i); + else + c.emplace(i, i); + } + + for (auto _ : st) { + benchmark::DoNotOptimize(c); + for_each(c, [](auto v) { benchmark::DoNotOptimize(v); }); + } + }) + ->Arg(8) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(8192); + }; + associative_ranges_bm( + std::type_identity>{}, + std::false_type{}, + "rng::for_each(set::iterator)", + std::ranges::for_each); + associative_ranges_bm( + std::type_identity>{}, + std::false_type{}, + "rng::for_each(multiset::iterator)", + std::ranges::for_each); + associative_ranges_bm( + std::type_identity>{}, + std::true_type{}, + "rng::for_each(map::iterator)", + std::ranges::for_each); + associative_ranges_bm( + std::type_identity>{}, + std::true_type{}, + "rng::for_each(multimap::iterator)", + std::ranges::for_each); } // {std,ranges}::for_each for join_view diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp new file mode 100644 index 0000000000000..bd449380daf00 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +// template +// constexpr Function // constexpr since C++20 +// for_each(Iter first, Iter last, Function f); + +#include +#include +#include +#include + +template +void test_node_container(Converter conv) { + Container c; + using value_type = typename Container::value_type; + for (int i = 0; i != 10; ++i) + c.insert(conv(i)); + { // Simple check + int invoke_count = 0; + std::for_each(c.begin(), c.end(), [&c, &invoke_count](const value_type& i) { + assert(&i == &*std::next(c.begin(), invoke_count++)); + }); + assert(invoke_count == 10); + } + { // Make sure that a start within the container works as expected + int invoke_count = 1; + std::for_each(std::next(c.begin()), c.end(), [&c, &invoke_count](const value_type& i) { + assert(&i == &*std::next(c.begin(), invoke_count++)); + }); + assert(invoke_count == 10); + } + { // Make sure that a start within the container works as expected + int invoke_count = 2; + std::for_each(std::next(c.begin(), 2), c.end(), [&c, &invoke_count](const value_type& i) { + assert(&i == &*std::next(c.begin(), invoke_count++)); + }); + assert(invoke_count == 10); + } + { // Make sure that an end within the container works as expected + int invoke_count = 1; + std::for_each(std::next(c.begin()), std::prev(c.end()), [&c, &invoke_count](const value_type& i) { + assert(&i == &*std::next(c.begin(), invoke_count++)); + }); + assert(invoke_count == 9); + } + { // Make sure that an empty range works + int invoke_count = 0; + std::for_each(c.begin(), c.begin(), [&c, &invoke_count](const value_type& i) { + assert(&i == &*std::next(c.begin(), invoke_count++)); + }); + assert(invoke_count == 0); + } + { // Make sure that a single-element range works + int invoke_count = 0; + std::for_each(c.begin(), std::next(c.begin()), [&c, &invoke_count](const value_type& i) { + assert(&i == &*std::next(c.begin(), invoke_count++)); + }); + assert(invoke_count == 1); + } +} + +int main(int, char**) { + test_node_container >([](int i) { return i; }); + test_node_container >([](int i) { return i; }); + test_node_container >([](int i) { return std::make_pair(i, i); }); + test_node_container >([](int i) { return std::make_pair(i, i); }); + + return 0; +} diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.pass.cpp index 3db0bde75abd7..3c0ff75fc56c7 100644 --- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.pass.cpp +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.pass.cpp @@ -15,9 +15,7 @@ #include #include #include -#if __has_include() -# include -#endif +#include #include #include "test_macros.h" diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass copy.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass copy.cpp new file mode 100644 index 0000000000000..d84eaacd487bd --- /dev/null +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass copy.cpp @@ -0,0 +1,131 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +// template +// constexpr Function // constexpr since C++20 +// for_each(Iter first, Iter last, Function f); + +#include +#include +#include +#include + +template +void test_node_container(Converter conv) { + Container c; + using value_type = typename Container::value_type; + for (int i = 0; i != 10; ++i) + c.insert(conv(i)); + { // Simple check + { + int invoke_count = 0; + std::ranges::for_each(c.begin(), c.end(), [&c, &invoke_count](const value_type& i) { + assert(&i == &*std::next(c.begin(), invoke_count++)); + }); + assert(invoke_count == 10); + } + { + int invoke_count = 0; + std::ranges::for_each(c, [&c, &invoke_count](const value_type& i) { + assert(&i == &*std::next(c.begin(), invoke_count++)); + }); + assert(invoke_count == 10); + } + } + { // Make sure that a start within the container works as expected + { + int invoke_count = 1; + std::ranges::for_each(std::next(c.begin()), c.end(), [&c, &invoke_count](const value_type& i) { + assert(&i == &*std::next(c.begin(), invoke_count++)); + }); + assert(invoke_count == 10); + } + { + int invoke_count = 1; + std::ranges::for_each( + std::ranges::subrange(std::next(c.begin()), c.end()), + [&c, &invoke_count](const value_type& i) { assert(&i == &*std::next(c.begin(), invoke_count++)); }); + assert(invoke_count == 10); + } + } + { // Make sure that a start within the container works as expected + { + int invoke_count = 2; + std::ranges::for_each(std::next(c.begin(), 2), c.end(), [&c, &invoke_count](const value_type& i) { + assert(&i == &*std::next(c.begin(), invoke_count++)); + }); + assert(invoke_count == 10); + } + { + int invoke_count = 2; + std::ranges::for_each( + std::ranges::subrange(std::next(c.begin(), 2), c.end()), + [&c, &invoke_count](const value_type& i) { assert(&i == &*std::next(c.begin(), invoke_count++)); }); + assert(invoke_count == 10); + } + } + { // Make sure that an end within the container works as expected + { + int invoke_count = 1; + std::ranges::for_each(std::next(c.begin()), std::prev(c.end()), [&c, &invoke_count](const value_type& i) { + assert(&i == &*std::next(c.begin(), invoke_count++)); + }); + assert(invoke_count == 9); + } + { + int invoke_count = 1; + std::ranges::for_each( + std::ranges::subrange(std::next(c.begin()), std::prev(c.end())), + [&c, &invoke_count](const value_type& i) { assert(&i == &*std::next(c.begin(), invoke_count++)); }); + assert(invoke_count == 9); + } + } + { // Make sure that an empty range works + { + int invoke_count = 0; + std::ranges::for_each(c.begin(), c.begin(), [&c, &invoke_count](const value_type& i) { + assert(&i == &*std::next(c.begin(), invoke_count++)); + }); + assert(invoke_count == 0); + } + { + int invoke_count = 0; + std::ranges::for_each(std::ranges::subrange(c.begin(), c.begin()), [&c, &invoke_count](const value_type& i) { + assert(&i == &*std::next(c.begin(), invoke_count++)); + }); + assert(invoke_count == 0); + } + } + { // Make sure that a single-element range works + { + int invoke_count = 0; + std::ranges::for_each(c.begin(), std::next(c.begin()), [&c, &invoke_count](const value_type& i) { + assert(&i == &*std::next(c.begin(), invoke_count++)); + }); + assert(invoke_count == 1); + } + { + int invoke_count = 0; + std::ranges::for_each( + std::ranges::subrange(c.begin(), std::next(c.begin())), + [&c, &invoke_count](const value_type& i) { assert(&i == &*std::next(c.begin(), invoke_count++)); }); + assert(invoke_count == 1); + } + } +} + +int main(int, char**) { + test_node_container >([](int i) { return i; }); + test_node_container >([](int i) { return i; }); + test_node_container >([](int i) { return std::make_pair(i, i); }); + test_node_container >([](int i) { return std::make_pair(i, i); }); + + return 0; +}