Search code examples
c++c++20projection

How do class members behave like functions when used as projections?


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?


Solution

  • &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.