[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
Fri Dec 12 02:25:59 PST 2025


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/171984

None

>From 9bc5354a6311de133af94f7f33046639755864fe 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} | 25 +++++++++++++++++++
 2 files changed, 27 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} (90%)

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 90%
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..6c97b1a36f41e 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
@@ -156,6 +156,31 @@ void test_node_container(Converter conv) {
       assert(invoke_count == 1);
     }
   }
+
+  { // check that std::invoke is used
+    struct S {
+      int i;
+
+      void zero() {
+        i = 0;
+      }
+    };
+
+    { // Iterator overload
+      std::map<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[1].i == 0);
+      assert(a[3].i == 0);
+      assert(a[5].i == 0);
+    }
+    { // Range overload
+      std::map<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[1].i == 0);
+      assert(a[3].i == 0);
+      assert(a[5].i == 0);
+    }
+  }
 }
 
 int main(int, char**) {



More information about the libcxx-commits mailing list