When using std::function
to call a non-static member-function, we can pass either the object pointer or the object reference as the first parameter:
struct Foo {
void bar() const { std::cout << "Foo::bar called "<< std::endl; }
};
int main() {
Foo foo;
// version1: pass the object pointer
std::function<void(Foo*)> call_bar_by_pointer = &Foo::bar;
call_bar_by_pointer(&foo);
// or, version2: pass the object reference
std::function<void(Foo&)> call_bar_by_reference = &Foo::bar;
call_bar_by_reference(foo);
return 0;
}
In my previous understanding, the non-static member-function essentially has the object pointer as the first argument implicitly. So when it comes to std::function
for calling a non-static member-function, I also expect the first parameter in the parameter list to be the pointer to the object type (i.e., version1).
Can someone kindly explain why the reference type (i.e., version2) is also supported here, and how it works?
CodePudding user response:
std::function
can be used for any callable.
It uses the INVOKE definition, specifically:
INVOKE(f, t1, t2, ..., tN) is defined as follows:
- if f is a pointer to member function of class T:
- (1) if
std::is_base_of<T, std::remove_reference_t<decltype(t1)>>::value
istrue
, thenINVOKE(f, t1, t2, ..., tN)
is equivalent to(t1.*f)(t2, ..., tN)
- (2) otherwise, [...] (
std::reference_wrapper
specialization)- (3) otherwise, if t1 does not satisfy the previous items, then
INVOKE(f, t1, t2, ..., tN)
is equivalent to((*t1).*f)(t2, ..., tN)
.
Point (1) works when t1
is a simple value or a reference, and point (3) works when t1
is a pointer (could be a smart pointer too).
This is just how std::function
(and anything using INVOKE) is implemented. Without it, you cannot use the same syntax, you must use the pointer-to-member operator:
using BarType = void (Foo::*)() const;
BarType normal_function_pointer = &Foo::bar;
(foo.*normal_function_pointer)();
Note that std::function
even works with std::reference_wrapper
(point (2)), and it could work with other things, like double pointers, but that's just not implemented (and probably not useful).