So I recently had a question about using the ranges variant of the std::ranges::find
algorithm and the answer used a projection:
if (auto const it{std::ranges::find(iv, 1, &S::a)}; it != iv.end()) {
std::cout << "Found!\n" << "\n";
}
Specifically the &S::a
part.
Normally I have seen a lambda being used in this place, but here we use a pointer to a class member. Is this transformed into a lambda or is there some overload selection going on?
Can someone break down how this, seemingly magic, works?
&S::a
is a pointer to the data member of class S
. Just like pointers to member functions, they both can be invoked through specific class objects, so they are callables.
In c++20, constraint functions (which are under namespace ranges
) in <algorithm>
and range adaptors in <ranges>
(or most of the standard library) uniformly invoke callables through std::invoke
.
std::invoke
is a generalized interface for calling callables. In addition to calling ordinary functions with the common form of f(x)
, it can also handle pointers to data members/functions.
Let's go back to your example. Basically, ranges::find
determines whether the current element is equal to the value via the condition if (std::invoke(proj, *it) == value)
, where proj
is &S::a
and *it
returns an object of type S
.
So std::invoke(&S::a, obj_s)
returns a reference to the member a
of the obj_s
, which is a reference to an int
.