【问题标题】:How to convert std::bind (or lambda) to std::function in deduced context?如何在推导的上下文中将 std::bind (或 lambda) 转换为 std::function?
【发布时间】:2017-01-04 14:21:18
【问题描述】:

我正在尝试将指向成员函数的指针(使用 std::bind 或 lambdas)转换为 std::function。我的尝试(根据this answer on 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);
  } 
}

但我从编译器收到以下错误消息:

// 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

【问题讨论】:

  • @Holt 我认为 OP 想要推断结果类型和参数类型。我说的对吗?
  • @W.F.确切地说(我想稍后操纵它们)
  • 好的,你真的需要使用 lambdastd::bind 吗? toFunction(&amp;MyStruct::f, &amp;m) 不会好吗?由于“lambda 类型”和std::bind 的返回是实现定义的,因此总是很难使用它们来推断模板参数...
  • @Holt 我需要占位符,因为这个 f 方法是我原始代码中的一个回调(更复杂),我可以从该回调中获得一些价值。
  • @Patryk 您可以将占位符(和其他东西)从toFunction 转发到std::bind,从指向成员函数的指针推导出Args 比从std::bind 推导出来要容易得多的返回类型,看我的回答。

标签: c++ c++11 lambda template-meta-programming std-function


【解决方案1】:

很难从 lambda 或 std::bind 返回类型中推断出参数,因此您可能希望延迟 toFunction 内的绑定,例如与:

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)...));
}

这样,您可以使用以下方法检索std::function

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

【讨论】:

    【解决方案2】:

    我觉得你有点过分了。如果你真的想为你的f 指定一个明确的std::function 类型,你可以用一些技巧直接指定它:

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

    话虽如此,到目前为止最简单的事情是简单地使用auto f 而不指定类型。我假设您已经想到了这一点,但在您的情况下不会这样做,但无论如何我这样说是为了以防万一。

    【讨论】:

    • 我认为这样你需要知道参数的类型......
    • 啊,我明白了。上面的内容可以很容易地转移到您的 toFunction 调用中,并使用模板类型为您提供参数类型。让我修改答案来演示
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-06
    • 2021-09-18
    • 1970-01-01
    • 1970-01-01
    • 2022-11-04
    相关资源
    最近更新 更多