std::function 和 std::bind:如何使用函数模板加载 std::bind

问题描述

我使用一个函数模板来加载一个 std::bind obj,我的代码

#include <iostream>
#include <functional>

template<typename RT_,typename ...Args>
void installCallback(const char name[],const std::function<RT_(Args...)> &func)
{

}

int add(int a,int b)
{
    return a + b;
}

int main(int argc,char *argv[])
{

    installCallback("add01",std::bind(add,std::placeholders::_1,std::placeholders::_2));
    installCallback<int,int,int>("add01",std::placeholders::_2));// didn't work either

    // this work well
    std::function<int(int,int)> fun = std::bind(add,std::placeholders::_2);
    installCallback("add02",fun);
    return 0;
}

,我收到此错误

/home/tong/Documents/awesome_auto_drive/awe_auto/sample/module/main.cpp:20:90: error: no matching function for call to ‘installCallback(const char [6],std::_Bind_helper<false,int (&)(int,int),const std::_Placeholder<1>&,const std::_Placeholder<2>&>::type)’
   20 |     installCallback("add01",std::placeholders::_2)); 
      |                                                                                          ^
$./main.cpp:6:6: note: candidate: ‘template<class RT_,class ... Args> void installCallback(const char*,std::function<_Res(_ArgTypes ...)>&&)’
    6 | void installCallback(const char name[],std::function<RT_(Args...)> &&func)
      |      ^~~~~~~~~~~~~~~
$./main.cpp:6:6: note:   template argument deduction/substitution Failed:
$./main.cpp:20:90: note:   ‘std::_Bind_helper<false,const std::_Placeholder<2>&>::type’ {aka ‘std::_Bind<int (*(std::_Placeholder<1>,std::_Placeholder<2>))(int,int)>’} is not derived from ‘std::function<_Res(_ArgTypes ...)>’
   20 |     installCallback("add01",std::placeholders::_2));
      |                                                                                          ^
sample/module/CMakeFiles/module.dir/build.make:62: recipe for target 'sample/module/CMakeFiles/module.dir/main.cpp.o' Failed
make[2]: *** [sample/module/CMakeFiles/module.dir/main.cpp.o] Error 1
CMakeFiles/Makefile2:2665: recipe for target 'sample/module/CMakeFiles/module.dir/all' Failed
make[1]: *** [sample/module/CMakeFiles/module.dir/all] Error 2
Makefile:129: recipe for target 'all' Failed
make: *** [all] Error 2

谁能告诉我,发生了什么?

解决方法

您遇到了模板函数参数推导规则。 std::bind 不返回 std::function,您为 int 传递的 Args... 只是 Args... 的前缀。因此,C++ 尝试推断 Args... 的其余部分并因该错误而失败。

快速修复

template<class Sig>
void installCallback(const char name[],const std::function<Sig> &func)

然后

installCallback<int(int,int)>

这使得 installCallback 的主体失去了 Args...

现在

installCallback("add01",std::bind(add,std::placeholders::_1,std::placeholders::_2));

无法工作,因为 bind (a) 不是 std 函数,并且 (b) 不知道它的参数类型是什么。

这里也没有意义。

installCallback("add01",add);

在逻辑上是等价的,噪音较少。它仍然不会编译;但是在 中,这将:

installCallback("add01",std::function(add));

由于扣除指南。绑定版本不会。

作为一般规则,如果您使用的是 std 绑定,请改用 lambda。在 中,lambdas 可以机械地替换 95% 的绑定使用,而在 中它变成了 99.9%,剩下的 0.1% 是你可能不应该使用并且不知道存在的绑定功能(通常不会,直到它以令人惊讶的方式破坏您的代码;将 std bind 传递给 std bind)。

,

我喜欢@yakk-adam-nevraumont 的回答。我认为它非常清楚并且恰当地解释了事情;到目前为止的最佳答案,所以我赞成它。我能做出的唯一其他贡献是,我能够使用 boost 库来对每个表达式的打印类型名称进行去错乱,得出类似的结论(推导规则不产生 std::function)。请参阅下面从提供的示例 @JoeT 和工作片段 here 修改的代码。

您会注意到 fun1 的推导类型与 fun2 的显式类型不同。

#include <functional>
#include <iostream>
#include <typeinfo>
#include <boost/core/demangle.hpp>

template<typename RT_,typename ...Args>
void installCallback(const char name[],const std::function<RT_(Args...)> &func)
{

}

int add(int a,int b)
{
    return a + b;
}

int main(int argc,char *argv[])
{
    auto fun1 = std::bind(add,std::placeholders::_2);
    std::function<int(int,int)> fun2 = std::bind(add,std::placeholders::_2);
    char const* name = typeid( fun1 ).name();

    // Outputs: std::_Bind<int (*(std::_Placeholder<1>,std::_Placeholder<2>))(int,int)>
    std::cout << boost::core::demangle(typeid(fun1).name()) << std::endl;

    // Outputs: std::function<int (int,int)>
    std::cout << boost::core::demangle(typeid(fun2).name()) << std::endl;

    return 0;
}