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

1

11

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?

Cherie answered 5/10, 2023 at 17:20 Comment(0)
I
8

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

Inquiline answered 5/10, 2023 at 17:59 Comment(6)
The Wizard is back! Thanks again. This was exactly the level of detail I was hoping for. The missing piece for me was that the pointer to a data member is callable. Interesting property!Cherie
I wouldn't say that a pointer to data member is callable. It really isn't. You can however pass it to a function, and std::invoke has an overload that "does the right thing" with it.Glosseme
@super, Well, they are invokable through an instance of the class, which returns a reference to that instances' member. So quasi-callable, maybe?Cherie
@Glosseme -- a pointer-to-data-member is a callable object as defined in the C++ standard. std::invoke operates on callable objects.Perch
timsong-cpp.github.io/cppwp/n3337/function.objects#func.def-4Magneton
Thank you. Is this something quite new in C++? When this was introduced into the language? If back in nineties someone said "class data member is callable", we all would laugh. When, why and how has this changed in the language?Baer

© 2022 - 2025 — McMap. All rights reserved.