passing member function const and non-const overload to std::function

68 Views Asked by At

I was trying to get "more fluent" with function pointer manipulation and I've got an issue with passing a member function to std::function when there are two overload: const and non-const. Based on other stackoverflow posts, I eventually succeeded, using static_cast but it's all but readable.
Here is a dummy working example. The core of the issue is wrapping two member-function overloads (const and non-const):

#include <functional>

struct dummy {
    int val = 42;
    int get() const { return val; }
    int& get() { return val; }
};

int main() {
    dummy obj;
    // std::function<int& (dummy &)> f = &dummy::get;
    // std::function<int (dummy const &)> f = &dummy::get;
    std::function<int&(dummy&)> f = static_cast<int& (dummy::*)()>(&dummy::get);
    std::function<int(dummy const&)> fc =
        static_cast<int (dummy::*)() const>(&dummy::get);

    // just to check that it works as expected
    f(obj) = 36;
    return fc(obj); // ok retrives 36
}

Live
Is there a simpler syntax?

2

There are 2 best solutions below

0
463035818_is_not_an_ai On

You can use a lambda expression:

std::function<int&(dummy&)> f = [](dummy& d) -> int& { return d.get(); };    
std::function<int(dummy const&)> fc = [](const dummy& d) { return d.get(); };

I didn't manage to convince gcc to accept the first line without explicitly specifiying int& return type tough.

Live

If you want to actaully stay with function pointers, I am not aware of something "simpler". Imho the static_cast is very clear about what is going on. It is possible to use type aliases to make it less verbose, but it will most certainly also be less readable.

0
Jarod42 On

Addresses of overload set cannot be reduced for std::function, but you can write helpers to simplify the "cast":

template <typename Ret, typename C, typename...Ts>
auto overload(Ret(C::*m)(Ts...)) { return m; }

template <typename Ret, typename C, typename...Ts>
auto const_overload(Ret(C::*m)(Ts...) const) { return m; }

then

std::function<int& (dummy &)> f = overload(&dummy::get);
std::function<int (dummy const &)> fc = const_overload(&dummy::get);

Demo

Those helpers won't help to differentiate void C::foo(int)/void C::foo(float) though and would requires other helpers.