Home > Enterprise >  Calling a member-function by std::function with reference to the object type as first parameter
Calling a member-function by std::function with reference to the object type as first parameter

Time:12-05

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 is true, then INVOKE(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).

  • Related