[libcxx-commits] [libcxx] 57aab63 - [libc++] Fix std::for_each(associative-container) not using std:invoke and projections (#171984)
via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Dec 15 03:06:04 PST 2025
Author: Nikolas Klauser
Date: 2025-12-15T12:05:59+01:00
New Revision: 57aab634179fc68012989a31eea5efec29f5d5bc
URL: https://github.com/llvm/llvm-project/commit/57aab634179fc68012989a31eea5efec29f5d5bc
DIFF: https://github.com/llvm/llvm-project/commit/57aab634179fc68012989a31eea5efec29f5d5bc.diff
LOG: [libc++] Fix std::for_each(associative-container) not using std:invoke and projections (#171984)
#164405 added specializations of `for_each` that didn't do the ranges
call shenanigans, but instead just did what the classic algorithms have
to do. This updates the calls to work for the ranges overloads as well.
Added:
libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
Modified:
libcxx/include/__tree
Removed:
libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass copy.cpp
################################################################################
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index 6f81c6d2bf9fd..0f6b2a84418b9 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..637a2eda76529 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,80 @@ 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