2

I'm trying to get the following to compile (g++-11.2, C++20), but I get:

error: no matching function for call to '__invoke(std::_Mem_fn<void (Foo::*)(int, double)>, std::__tuple_element_t<0, std::tuple<int, double> >, std::__tuple_element_t<1, std::tuple<int, double> >)'
 1843 |       return std::__invoke(std::forward<_Fn>(__f),

Code:

#include <iostream>
#include <tuple>

struct Foo
{
    void bar(const int x, const double y) 
    {  
        std::cout << x << " " << y << std::endl;
    }  


    void bar_apply()
    {  
        // fails
        std::apply(std::mem_fn(&Foo::bar), std::tuple<int, double>(1, 5.0));
    }  
};


int main()
{
    Foo foo;
    foo.bar_apply();
};
Agrim Pathak
  • 3,047
  • 4
  • 27
  • 43

3 Answers3

6

I recommend using C++20 bind_front, which is more lightweight and intuitive. Just like its name, member functions require a specific class object to invoke, so you need to bind this pointer to Foo::bar.

void bar_apply()
{  
  std::apply(std::bind_front(&Foo::bar, this), std::tuple<int, double>(1, 5.0));
}

Demo.

康桓瑋
  • 33,481
  • 5
  • 40
  • 90
  • This is nice, but I've heard the `std::bind` doesn't have optimal performance. Do you know if that's the same story with `std::bind_front`? – Agrim Pathak Oct 24 '21 at 05:27
  • 1
    One of the purposes of the introduction of `std::bind_front` is to solve the problem of insufficient performance of `std::bind`. Therefore, it is more lightweight and efficient. – 康桓瑋 Oct 24 '21 at 05:29
2

std::mem_fn(&Foo::bar) does not store the instance of Foo like a capturing lambda would do:

std::apply([this](int x, double y) { return bar(x,y); },
           std::tuple<int, double>(1, 5.0));

You therefore need to supply the instance when calling the mem_fn object:

std::apply(std::mem_fn(&Foo::bar), std::tuple<Foo*, int, double>(this, 1, 5.0));

or simpler, using make_tuple:

std::apply(std::mem_fn(&Foo::bar), std::make_tuple(this, 1, 5.0));
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
2

As others have pointed out, you need an instance to call the member function. This you can deliver to std::apply by wrapping the function call to a lambda expression or via tuple arguments.

However, I would suggest std::invoke, which is more apt to this task. By this way, no instance wrapping is required.

#include <functional> // std::invoke

std::invoke(&Foo::bar, this, 1, 5.0);
JeJo
  • 30,635
  • 6
  • 49
  • 88