[libcxx-commits] [libcxx] [libc++] Optimize ranges::for_each for iterating over __trees (PR #164405)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Nov 25 05:27:33 PST 2025
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/164405
>From fae64c3572f8d964c0898ece297f4ccaddb9dbd1 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 21 Oct 2025 14:07:25 +0200
Subject: [PATCH] [libc++] Optimize ranges::for_each for iterating over __trees
[libc++] Optimize std::for_each for __tree iterators
---
libcxx/include/__algorithm/for_each.h | 8 +-
libcxx/include/__algorithm/ranges_for_each.h | 10 +-
.../__algorithm/specialized_algorithms.h | 4 +
libcxx/include/__tree | 95 +++++++++++++++++++
libcxx/include/map | 37 ++++++++
libcxx/include/set | 37 ++++++++
.../nonmodifying/for_each.bench.cpp | 63 ++++++++++--
.../alg.foreach/for_each.associative.pass.cpp | 77 +++++++++++++++
.../alg.foreach/for_each.pass.cpp | 4 +-
9 files changed, 323 insertions(+), 12 deletions(-)
create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp
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 <class _InputIterator, class _Sent, class _Func, class _Proj>
_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..bc618442b9791 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<projected<iterator_t<_Range>, _Proj>> _Func>
_LIBCPP_HIDE_FROM_ABI constexpr for_each_result<borrowed_iterator_t<_Range>, _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, remove_cvref_t<_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..6402ba36b0962 100644
--- a/libcxx/include/__algorithm/specialized_algorithms.h
+++ b/libcxx/include/__algorithm/specialized_algorithms.h
@@ -19,11 +19,15 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace _Algorithm {
struct __fill_n {};
+struct __for_each {};
} // namespace _Algorithm
template <class>
struct __single_iterator;
+template <class, class>
+struct __iterator_pair;
+
// 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..9406a7a79c32e 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>
@@ -717,6 +718,63 @@ private:
friend class __tree_const_iterator;
};
+template <class _Reference, class _Break, class _NodePtr, class _Func, class _Proj>
+_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;
+}
+
+template <class _Reference, class _NodePtr, class _EndNodePtr, class _Func, class _Proj>
+_LIBCPP_HIDE_FROM_ABI void
+__tree_iterate_from_begin(_EndNodePtr __first, _EndNodePtr __last, _Func& __func, _Proj& __proj) {
+ 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));
+ }
+ }
+}
+
+#ifndef _LIBCPP_CXX03_LANG
+template <class _Tp, class _NodePtr, class _DiffType>
+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 <class _Func, class _Proj>
+ _LIBCPP_HIDE_FROM_ABI static void operator()(__iterator __first, __iterator __last, _Func& __func, _Proj& __proj) {
+ std::__tree_iterate_from_begin<typename __iterator::reference, _NodePtr>(
+ __first.__ptr_, __last.__ptr_, __func, __proj);
+ }
+};
+#endif
+
template <class _Tp, class _NodePtr, class _DiffType>
class __tree_const_iterator {
using _NodeTypes _LIBCPP_NODEBUG = __tree_node_types<_NodePtr>;
@@ -780,7 +838,27 @@ private:
template <class, class, class>
friend class __tree;
+
+ friend struct __specialized_algorithm<_Algorithm::__for_each,
+ __iterator_pair<__tree_const_iterator, __tree_const_iterator> >;
+};
+
+#ifndef _LIBCPP_CXX03_LANG
+template <class _Tp, class _NodePtr, class _DiffType>
+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 = __tree_const_iterator<_Tp, _NodePtr, _DiffType>;
+
+ template <class _Func, class _Proj>
+ _LIBCPP_HIDE_FROM_ABI static void operator()(__iterator __first, __iterator __last, _Func& __func, _Proj& __proj) {
+ std::__tree_iterate_from_begin<typename __iterator::reference, _NodePtr>(
+ __first.__ptr_, __last.__ptr_, __func, __proj);
+ }
};
+#endif
template <class _Tp, class _Compare>
#ifndef _LIBCPP_CXX03_LANG
@@ -1484,7 +1562,24 @@ 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, __tree>;
+};
+
+#if _LIBCPP_STD_VER >= 14
+template <class _Tp, class _Compare, class _Allocator>
+struct __specialized_algorithm<_Algorithm::__for_each, __tree<_Tp, _Compare, _Allocator> > {
+ static const bool __has_algorithm = true;
+
+ using __node_pointer _LIBCPP_NODEBUG = typename __tree<_Tp, _Compare, _Allocator>::__node_pointer;
+
+ template <class _Tree, class _Func, class _Proj>
+ _LIBCPP_HIDE_FROM_ABI static auto operator()(_Tree&& __range, _Func __func, _Proj __proj) {
+ std::__tree_iterate_from_root([](__node_pointer) { return false; }, __range.__root(), __func, __proj);
+ return std::make_pair(__range.end(), std::move(__func));
+ }
};
+#endif
// Precondition: __size_ != 0
template <class _Tp, class _Compare, class _Allocator>
diff --git a/libcxx/include/map b/libcxx/include/map
index 0dca11cabd12e..9cbe78d667adf 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -577,6 +577,7 @@ erase_if(multimap<Key, T, Compare, Allocator>& 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, map>;
};
# if _LIBCPP_STD_VER >= 17
@@ -1422,6 +1425,22 @@ map(initializer_list<pair<_Key, _Tp>>, _Allocator)
-> map<remove_const_t<_Key>, _Tp, less<remove_const_t<_Key>>, _Allocator>;
# endif
+# if _LIBCPP_STD_VER >= 14
+template <class _Key, class _Tp, class _Compare, class _Allocator>
+struct __specialized_algorithm<_Algorithm::__for_each, map<_Key, _Tp, _Compare, _Allocator>> {
+ using __map _LIBCPP_NODEBUG = map<_Key, _Tp, _Compare, _Allocator>;
+
+ static const bool __has_algorithm = true;
+
+ template <class _Map, class _Func>
+ _LIBCPP_HIDE_FROM_ABI static auto operator()(_Map&& __map, _Func __func) {
+ auto [_, __func2] = __specialized_algorithm<_Algorithm::__for_each, typename __map::__base>()(
+ __map.__tree_, std::move(__func));
+ return std::make_pair(__map.end(), std::move(__func2));
+ }
+};
+# endif
+
# ifndef _LIBCPP_CXX03_LANG
template <class _Key, class _Tp, class _Compare, class _Allocator>
_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, multimap>;
};
# if _LIBCPP_STD_VER >= 17
@@ -1972,6 +1993,22 @@ multimap(initializer_list<pair<_Key, _Tp>>, _Allocator)
-> multimap<remove_const_t<_Key>, _Tp, less<remove_const_t<_Key>>, _Allocator>;
# endif
+# if _LIBCPP_STD_VER >= 14
+template <class _Key, class _Tp, class _Compare, class _Allocator>
+struct __specialized_algorithm<_Algorithm::__for_each, multimap<_Key, _Tp, _Compare, _Allocator>> {
+ using __map _LIBCPP_NODEBUG = multimap<_Key, _Tp, _Compare, _Allocator>;
+
+ static const bool __has_algorithm = true;
+
+ template <class _Map, class _Func>
+ _LIBCPP_HIDE_FROM_ABI static auto operator()(_Map&& __map, _Func __func) {
+ auto [_, __func2] = __specialized_algorithm<_Algorithm::__for_each, typename __map::__base>()(
+ __map.__tree_, std::move(__func));
+ return std::make_pair(__map.end(), std::move(__func2));
+ }
+};
+# endif
+
template <class _Key, class _Tp, class _Compare, class _Allocator>
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..59742762ae841 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -518,6 +518,7 @@ erase_if(multiset<Key, Compare, Allocator>& 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 <class, class...>
+ friend struct __specialized_algorithm;
};
# if _LIBCPP_STD_VER >= 17
@@ -944,6 +948,21 @@ template <class _Key, class _Allocator, class = enable_if_t<__is_allocator_v<_Al
set(initializer_list<_Key>, _Allocator) -> set<_Key, less<_Key>, _Allocator>;
# endif
+# if _LIBCPP_STD_VER >= 14
+template <class _Alg, class _Key, class _Compare, class _Allocator>
+struct __specialized_algorithm<_Alg, set<_Key, _Compare, _Allocator>> {
+ using __set _LIBCPP_NODEBUG = set<_Key, _Compare, _Allocator>;
+
+ static const bool __has_algorithm = __specialized_algorithm<_Alg, typename __set::__base>::__has_algorithm;
+
+ // set's begin() and end() are identical with and without const qualification
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI static auto operator()(const __set& __set, _Args&&... __args) {
+ return __specialized_algorithm<_Alg, typename __set::__base>()(__set.__tree_, std::forward<_Args>(__args)...);
+ }
+};
+# endif
+
template <class _Key, class _Compare, class _Allocator>
inline _LIBCPP_HIDE_FROM_ABI bool
operator==(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
@@ -1342,6 +1361,9 @@ public:
return __tree_.__equal_range_multi(__k);
}
# endif
+
+ template <class, class...>
+ friend struct __specialized_algorithm;
};
# if _LIBCPP_STD_VER >= 17
@@ -1389,6 +1411,21 @@ template <class _Key, class _Allocator, class = enable_if_t<__is_allocator_v<_Al
multiset(initializer_list<_Key>, _Allocator) -> multiset<_Key, less<_Key>, _Allocator>;
# endif
+# if _LIBCPP_STD_VER >= 14
+template <class _Alg, class _Key, class _Compare, class _Allocator>
+struct __specialized_algorithm<_Alg, multiset<_Key, _Compare, _Allocator>> {
+ using __set _LIBCPP_NODEBUG = multiset<_Key, _Compare, _Allocator>;
+
+ static const bool __has_algorithm = __specialized_algorithm<_Alg, typename __set::__base>::__has_algorithm;
+
+ // set's begin() and end() are identical with and without const qualification
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI static auto operator()(const __set& __set, _Args&&... __args) {
+ return __specialized_algorithm<_Alg, typename __set::__base>()(__set.__tree_, std::forward<_Args>(__args)...);
+ }
+};
+# endif
+
template <class _Key, class _Compare, class _Allocator>
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..6313a36cbd31d 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 <cstddef>
#include <deque>
#include <list>
+#include <map>
#include <ranges>
#include <string>
+#include <type_traits>
#include <vector>
#include <benchmark/benchmark.h>
@@ -23,7 +25,7 @@ int main(int argc, char** argv) {
// {std,ranges}::for_each
{
- auto bm = []<class Container>(std::string name, auto for_each) {
+ auto sequence_bm = []<class Container>(std::string name, auto for_each) {
using ElemType = typename Container::value_type;
benchmark::RegisterBenchmark(
name,
@@ -44,12 +46,59 @@ int main(int argc, char** argv) {
->Arg(50) // non power-of-two
->Arg(8192);
};
- bm.operator()<std::vector<int>>("std::for_each(vector<int>)", std_for_each);
- bm.operator()<std::deque<int>>("std::for_each(deque<int>)", std_for_each);
- bm.operator()<std::list<int>>("std::for_each(list<int>)", std_for_each);
- bm.operator()<std::vector<int>>("rng::for_each(vector<int>)", std::ranges::for_each);
- bm.operator()<std::deque<int>>("rng::for_each(deque<int>)", std::ranges::for_each);
- bm.operator()<std::list<int>>("rng::for_each(list<int>)", std::ranges::for_each);
+ sequence_bm.operator()<std::vector<int>>("std::for_each(vector<int>)", std_for_each);
+ sequence_bm.operator()<std::deque<int>>("std::for_each(deque<int>)", std_for_each);
+ sequence_bm.operator()<std::list<int>>("std::for_each(list<int>)", std_for_each);
+ sequence_bm.operator()<std::vector<int>>("rng::for_each(vector<int>)", std::ranges::for_each);
+ sequence_bm.operator()<std::deque<int>>("rng::for_each(deque<int>)", std::ranges::for_each);
+ sequence_bm.operator()<std::list<int>>("rng::for_each(list<int>)", std::ranges::for_each);
+
+ auto associative_bm = []<class Container>(std::type_identity<Container>, std::string name, auto for_each) {
+ benchmark::RegisterBenchmark(
+ name,
+ [for_each](auto& st) {
+ Container c;
+ for (int64_t i = 0; i != st.range(0); ++i) {
+ if constexpr (std::is_same_v<typename Container::value_type, int>)
+ 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::set<int>>{}, "rng::for_each(set<int>::iterator)", std::ranges::for_each);
+ associative_bm(std::type_identity<std::multiset<int>>{}, "rng::for_each(multiset<int>::iterator)", std::ranges::for_each);
+ associative_bm(std::type_identity<std::map<int, int>>{}, "rng::for_each(map<int>::iterator)", std::ranges::for_each);
+ associative_bm(std::type_identity<std::multimap<int, int>>{}, "rng::for_each(multimap<int>::iterator)", std::ranges::for_each);
+
+ auto associative_ranges_bm = []<class Container>(std::type_identity<Container>, std::string name, auto for_each) {
+ benchmark::RegisterBenchmark(
+ name,
+ [for_each](auto& st) {
+ Container c;
+ for (int64_t i = 0; i != st.range(0); ++i)
+ c.insert(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::set<int>>{}, "rng::for_each(set<int>)", 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..dabaab37a2e5f
--- /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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// template<InputIterator Iter, Callable<auto, Iter::reference> Function>
+// constexpr Function // constexpr since C++20
+// for_each(Iter first, Iter last, Function f);
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <set>
+
+template <class Container, class Converter>
+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<std::set<int> >([](int i) { return i; });
+ test_node_container<std::multiset<int> >([](int i) { return i; });
+ test_node_container<std::map<int, int> >([](int i) { return std::make_pair(i, i); });
+ test_node_container<std::multimap<int, int> >([](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 <algorithm>
#include <cassert>
#include <deque>
-#if __has_include(<ranges>)
-# include <ranges>
-#endif
+#include <ranges>
#include <vector>
#include "test_macros.h"
More information about the libcxx-commits
mailing list