How to convert std::bind (or lambda) to std::function in deduced context?

1.1k views Asked by At

I am trying to convert pointer to member function (using either std::bind or lambdas) to a std::function. My attempt (following an answer from this answer on SO) looks like so:

#include <functional>

template<typename T>
struct AsFunction :
  AsFunction<decltype(&T::operator())>
{};

template<class ReturnType, class... Args>
struct AsFunction<ReturnType(Args...)> {
  using type = std::function<ReturnType(Args...)>;
};

template<class ReturnType, class... Args>
struct AsFunction<ReturnType(*)(Args...)> {
  using type = std::function<ReturnType(Args...)>;
};

template<class Class, class ReturnType, class... Args>
struct AsFunction<ReturnType(Class::*)(Args...) const> {
  using type = std::function<ReturnType(Args...)>;
};

template<class F>
auto toFunction( F f ) -> typename AsFunction<F>::type {
  return {f};
}

struct MyStruct {
  int x,y;
  void f(int){};
};

int main(){
  MyStruct m;

  {
    // this works
    auto f = std::bind(&MyStruct::f, &m, std::placeholders::_1);
    f(2);
  }

  {
    // this doesn't
    auto f = toFunction(std::bind(&MyStruct::f, &m, std::placeholders::_1));
    f(2);                                                                                                                                                                                                                                                                      
  }

  { 
    // .. neither does this
    auto f = toFunction([m](int x) mutable { m.f(x); });
    f(2);
  } 
}

but I get the following error messages from the compiler:

// first not working 
main.cpp:24:6: note:   substitution of deduced template arguments resulted in errors seen above
main.cpp: In instantiation of ‘struct AsFunction<std::_Bind<std::_Mem_fn<void (MyStruct::*)(int)>(MyStruct*, std::_Placeholder<1>)> >’:
main.cpp:24:6:   required by substitution of ‘template<class F> typename AsFunction<F>::type toFunction(F) [with F = std::_Bind<std::_Mem_fn<void (MyStruct::*)(int)>(MyStruct*, std::_Placeholder<1>)>]’
main.cpp:44:75:   required from here
main.cpp:4:8: error: decltype cannot resolve address of overloaded function
 struct AsFunction :
        ^~~~~~~~~~
main.cpp: In function ‘int main()’:
main.cpp:44:75: error: no matching function for call to ‘toFunction(std::_Bind_helper<false, void (MyStruct::*)(int), MyStruct*, const std::_Placeholder<1>&>::type)’
     auto f = toFunction(std::bind(&MyStruct::f, &m, std::placeholders::_1));
                                                                           ^
main.cpp:24:6: note: candidate: template<class F> typename AsFunction<F>::type toFunction(F)
 auto toFunction( F f ) -> typename AsFunction<F>::type {
      ^~~~~~~~~~
main.cpp:24:6: note:   substitution of deduced template arguments resulted in errors seen above

// second non working braces with lambda
main.cpp: In instantiation of ‘struct AsFunction<void (main()::<lambda(int)>::*)(int)>’:
main.cpp:4:8:   required from ‘struct AsFunction<main()::<lambda(int)> >’
main.cpp:24:6:   required by substitution of ‘template<class F> typename AsFunction<F>::type toFunction(F) [with F = main()::<lambda(int)>]’
main.cpp:50:55:   required from here
main.cpp:5:23: error: ‘operator()’ is not a member of ‘void (main()::<lambda(int)>::*)(int)’
   AsFunction<decltype(&T::operator())>
                       ^~
main.cpp:50:55: error: no matching function for call to ‘toFunction(main()::<lambda(int)>)’
     auto f = toFunction([m](int x) mutable { m.f(x); });
                                                       ^
main.cpp:24:6: note: candidate: template<class F> typename AsFunction<F>::type toFunction(F)
 auto toFunction( F f ) -> typename AsFunction<F>::type {
      ^~~~~~~~~~
main.cpp:24:6: note:   substitution of deduced template arguments resulted in errors seen above
2

There are 2 answers

0
Holt On BEST ANSWER

It is hard to deduce arguments from a lambda or a std::bind return type, so you may want to delay the bind inside toFunction, e.g. with:

template<typename C, typename Ret, typename... Args, typename... BArgs>
auto toFunction(Ret (C::*f)(Args...), BArgs&&... bargs) {
  return std::function<Ret(Args...)>(std::bind(f, std::forward<BArgs>(bargs)...));
}

This way, you can retrieve a std::function using:

auto f = toFunction(&MyStruct::f, &m, std::placeholders::_1);
f(2); 
2
Smeeheey On

I think you're overkilling this a bit. If you really want an explicit std::function type for your f, you can just specify it directly with some trickery:

int main(){
  MyStruct m;

  {
    std::function<decltype(std::declval<MyStruct>().f(int()))(int)> f(std::bind(&MyStruct::f, &m, std::placeholders::_1));
    f(2);                                                                                                                                                                                                                                                                      
  }
}

Having said that, the simplest thing by far is to simply use auto f without specifying a type. I'm assuming you've already thought of that and it won't do in your case, but I say this just in case anyway.