Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing an overloaded member function to function template

I would like to have a function, that calls a given member function with the provides variadic input argument. I wrote something like this:

#include <type_traits>
#include <utility>    

struct A {
    constexpr int show(int a, int b) const noexcept {return a + b;}
};

template <typename T, typename MemFn, typename ... Args>
int show(T && obj, MemFn Fn, Args&&... args)
{
    return (obj.*Fn)(std::forward<Args>(args)...);
}

int main()
{
    constexpr A a;
    return show(a, &A::show, 1, 2);
}

and it works just fine, as long as I only have one definition of show method in my struct. As soon as I add something like

struct A {
    constexpr int show(int a, int b) const noexcept {return a + b;}
    constexpr int show(int a) const noexcept {return a * 3;}
};

The compiler can not deduce the type of the member function and it really makes all the sense, but I was wondering if there is a workaround for this problem, like embedding the input arguments types in member function template or something?

Sample code can be found here.

like image 731
apramc Avatar asked Apr 29 '26 21:04

apramc


1 Answers

This is an annoyingly difficult problem, which continuously leads to language proposals in an attempt to address it (P0119, P0834, P1170).

Until then, the question of how to wrap invoking a particular member function on a type, where that member function is either overloaded or a template or takes default arguments, is pretty difficult.

The easiest way to do this is just to write a lambda:

[](A& a, auto&&... args) -> decltype(a.show(FWD(args)...)) { return a.show(FWD(args)...); }

But this is actually not that easy, nor is it particularly convenient - and it really only handles the case where show is invokable on a non-const A. What if we had const and non-const overloads? Or & and &&?

The most complete way to implement this, in my opinion, is to use Boost.HOF with this macro:

#define CLASS_MEMBER(T, mem) boost::hof::fix(boost::hof::first_of(\
    boost::hof::match(                                            \
        [](auto, T& s, auto&&... args)                            \
            BOOST_HOF_RETURNS(s.mem(FWD(args)...)),               \
        [](auto, T&& s, auto&&... args)                           \
            BOOST_HOF_RETURNS(std::move(s).mem(FWD(args)...)),    \
        [](auto, T const&& s, auto&&... args)                     \
            BOOST_HOF_RETURNS(std::move(s).mem(FWD(args)...)),    \
        [](auto, T const& s, auto&&... args)                      \
            BOOST_HOF_RETURNS(s.mem(FWD(args)...))),              \
    [](auto self, auto&& this_, auto&&... args)                   \
        BOOST_HOF_RETURNS(self(*FWD(this_), FWD(args)...))        \
    ))

which in your case, you want: CLASS_MEMBER(A, show). That will give you a function object that you can properly invoke:

auto show_fn = CLASS_MEMBER(A, show);
show_fn(a, 1);       // ok, calls a.show(1)
show_fn(a, 1, 2);    // ok, calls a.show(1, 2)
show_fn(a, 1, 2, 3); // error, no matching call - but sfinae friendly
like image 190
Barry Avatar answered May 02 '26 11:05

Barry



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!