Must I use lambda to pass a member function as std::function?
Asked Answered
J

3

6

The following code works, but I feel that the line worker([this](int a, long b, int* c){receiver(a, b, c);}); is sort of redundant because it is repeating the signature of receiver. Instead of passing a lambda function that in turn calls the member function, can I somehow pass the member function directly?

using callback = std::function<void(int, long, int*)>;

void worker(callback c)
{
    c(1,2L,(int*)3);
}

class Caller
{
public:
    Caller()
    {
        worker([this](int a, long b, int* c){receiver(a, b, c);});
    }

    void receiver(int a, long b, int* c)
    {
    }
};
Jae answered 4/12, 2021 at 20:40 Comment(2)
en.cppreference.com/w/cpp/utility/functional/mem_fnJaysonjaywalk
en.cppreference.com/w/cpp/utility/functional/bind_frontJaysonjaywalk
T
3

std::bind is the classical approach, that avoids the need to explicitly spell out the forwarding signature:

using namespace std::placeholders;

// ...

    worker(std::bind(&Caller::receiver, this, _1, _2, _3));

C++20 also has std::bind_front; it reduces this verbiage, somewhat.

You cannot pass a pointer to a member function "directly". This is because a member function requires a specific instance of an object whose member function should get invoked. So, in some form of fashion, in some way, you cannot avoid this, or some other instance of the object, to be involved in the process. This is fundamental to C++. The only thing that can be done here is to find some syntactic sugar, which is basically all this is, here.

Talithatalk answered 4/12, 2021 at 20:59 Comment(1)
though std::bind is considered outdated and clang-tidy will mark this code with a warning. Also, placeholders: 1) hide arguments' types from the reader 2) hide arguments' types from the compiler thus introducing the need for "manual" resolution which will look waaaay uglier than forwardingWeasner
N
5

If you have access to C++20, use bolov's answer. If not…

Though this still uses a lambda, you can take advantage of an argument pack to avoid duplicating the signature:

worker([this](auto... params) { receiver(params...); });

This requires C++14 or newer.

Perfect forwarding can be added if you have more complex types and want to avoid copies:

worker([this](auto&&... params) {
    receiver(std::forward<decltype(params)>(params)...);
});
Norvin answered 4/12, 2021 at 21:9 Comment(0)
J
4

The cleanest way is C++20's std::bind_front:

worker(std::bind_front(&Caller::receiver, this));

Why use `std::bind_front` over lambdas in C++20?

Jaysonjaywalk answered 4/12, 2021 at 21:24 Comment(2)
This seems to be the cleanest solution, and it worked in VC++ 2022, but unfortunately the compiler I am using (ESP8266 SDK) seems to be supporting only C++17, and it does not seem to have bind_front but only bind. So close...Jae
@DamnVegetables yes, C++20 is pretty new. Keep this solution in mind for the future :pJaysonjaywalk
T
3

std::bind is the classical approach, that avoids the need to explicitly spell out the forwarding signature:

using namespace std::placeholders;

// ...

    worker(std::bind(&Caller::receiver, this, _1, _2, _3));

C++20 also has std::bind_front; it reduces this verbiage, somewhat.

You cannot pass a pointer to a member function "directly". This is because a member function requires a specific instance of an object whose member function should get invoked. So, in some form of fashion, in some way, you cannot avoid this, or some other instance of the object, to be involved in the process. This is fundamental to C++. The only thing that can be done here is to find some syntactic sugar, which is basically all this is, here.

Talithatalk answered 4/12, 2021 at 20:59 Comment(1)
though std::bind is considered outdated and clang-tidy will mark this code with a warning. Also, placeholders: 1) hide arguments' types from the reader 2) hide arguments' types from the compiler thus introducing the need for "manual" resolution which will look waaaay uglier than forwardingWeasner

© 2022 - 2025 — McMap. All rights reserved.