[libcxx-commits] [libcxx] [libc++] Fix std::for_each(associative-container) not using std:invoke and projections (PR #171984)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Dec 15 01:47:34 PST 2025
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/171984
>From b32058c51e76a3d5d7023a8054cf5a8db87afc27 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 12 Dec 2025 11:25:04 +0100
Subject: [PATCH] [libc++] Fix std::for_each(associative-container) not using
std:invoke and projections
---
libcxx/include/__tree | 4 +-
...p => ranges.for_each.associative.pass.cpp} | 73 +++++++++++++++++++
2 files changed, 75 insertions(+), 2 deletions(-)
rename libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/{ranges.for_each.associative.pass copy.cpp => ranges.for_each.associative.pass.cpp} (76%)
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index 22aa186470bda..2b93ea6603737 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -670,7 +670,7 @@ bool __tree_iterate_from_root(_Break __break, _NodePtr __root, _Func& __func, _P
}
if (__break(__root))
return true;
- __func(static_cast<_Reference>(__root->__get_value()));
+ std::__invoke(__func, std::__invoke(__proj, 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;
@@ -690,7 +690,7 @@ __tree_iterate_subrange(_NodeIter __first_it, _NodeIter __last_it, _Func& __func
if (__first == __last)
return;
const auto __nfirst = static_cast<_NodePtr>(__first);
- __func(static_cast<_Reference>(__nfirst->__get_value()));
+ std::__invoke(__func, std::__invoke(__proj, static_cast<_Reference>(__nfirst->__get_value())));
if (__nfirst->__right_) {
if (std::__tree_iterate_from_root<_Reference>(
[&](_NodePtr __node) -> bool { return __node == __last; },
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.cpp
similarity index 76%
rename from libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass copy.cpp
rename to libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
index b78adcc461ed1..11370069612e7 100644
--- 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.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// <algorithm>
+// UNSUPPORTED: c++03, c++11, c++14, c++17
// Check that the special implementation of ranges::for_each for the associative container iterators works as expected
@@ -158,11 +159,83 @@ void test_node_container(Converter conv) {
}
}
+template <template <class> class Container>
+void test_invoke_set_like() {
+
+ { // check that std::invoke is used
+ struct T {
+ mutable int i = 3;
+
+ void zero() const { i = 0; }
+ };
+
+ class S {
+ int val_;
+
+ public:
+ S(int val) : val_(val) {}
+
+ T j;
+
+ bool operator<(const S& rhs) const { return val_ < rhs.val_; }
+ };
+
+ { // Iterator overload
+ Container<S> a = {S{2}, S{4}, S{6}};
+ std::ranges::for_each(a.begin(), a.end(), &T::zero, &S::j);
+ assert(a.find(2)->j.i == 0);
+ assert(a.find(4)->j.i == 0);
+ assert(a.find(6)->j.i == 0);
+ }
+ { // Range overload
+ Container<S> a = {S{2}, S{4}, S{6}};
+ std::ranges::for_each(a, &T::zero, &S::j);
+ assert(a.find(2)->j.i == 0);
+ assert(a.find(4)->j.i == 0);
+ assert(a.find(6)->j.i == 0);
+ }
+ }
+}
+
+template <template <class, class> class Container>
+void test_invoke_map_like() {
+
+ { // check that std::invoke is used
+ struct S {
+ int i;
+
+ void zero() { i = 0; }
+ };
+
+ { // Iterator overload
+ Container<int, S> a = {{1, S{2}}, {3, S{4}}, {5, S{6}}};
+ std::ranges::for_each(a.begin(), a.end(), &S::zero, &std::pair<const int, S>::second);
+ assert(a.find(1)->second.i == 0);
+ assert(a.find(3)->second.i == 0);
+ assert(a.find(5)->second.i == 0);
+ }
+ { // Range overload
+ Container<int, S> a = {{1, S{2}}, {3, S{4}}, {5, S{6}}};
+ std::ranges::for_each(a, &S::zero, &std::pair<const int, S>::second);
+ assert(a.find(1)->second.i == 0);
+ assert(a.find(3)->second.i == 0);
+ assert(a.find(5)->second.i == 0);
+ }
+ }
+}
+
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); });
+ test_invoke_set_like<std::set>();
+ test_invoke_set_like<std::multiset>();
+
+ test_invoke_map_like<std::map>();
+ test_invoke_map_like<std::multimap>();
+
+
return 0;
}
More information about the libcxx-commits
mailing list