如何用std::bind绑定内部调用std::bind的函数?
使用C++17编译时,尝试用NoParameterBinding封装BindRequest::bindOnce时编译失败,报错无法转换为std::function<void()>。
已实现两种通用函数绑定方式:
NoParameterBinding:绑定函数与所有参数,生成可无参调用的std::function<void()>,运行正常;SingleParameterBinding:用std::placeholders::_1保留第一个参数,绑定其余参数,生成std::function<void(long)>,运行正常;BindRequest::bindOnce可转发参数给SingleParameterBinding::bind,运行正常;但BindRequest::bindTwice尝试将bindOnce作为参数传给NoParameterBinding::bind时编译报错。
编译器错误信息:
forwarding.cpp:10:38: error: conversion from ‘std::_Bind_helper<false, void (&)(void (&)(const long int&, const double&), double&&), void (&)(const long int&, const double&), double>::type’ to non-scalar type ‘NoParameterBinding::Task’ {aka ‘std::function<void()>’} requested
相关代码:
#include<functional> struct NoParameterBinding { using Task = std::function<void()>; template<typename FuncType, typename... Args> static void bind(FuncType&& func, Args&&... args) { Task task = std::bind(std::forward<FuncType>(func), std::forward<Args>(args)...); } }; struct SingleParameterBinding { using CallbackType = std::function<void(long)>; template<typename FuncType, typename... Args> static void bind(FuncType&& callbackFunc, Args&&... args) { CallbackType callback = std::bind(std::forward<FuncType>(callbackFunc), std::placeholders::_1, std::forward<Args>(args)...); } }; struct BindRequest { using CallbackType = SingleParameterBinding::CallbackType; template<typename CallbackFuncType, typename... CallbackArgs> static void bindOnce(CallbackFuncType&& callback, CallbackArgs&&... callbackArgs) { SingleParameterBinding::bind(std::forward<CallbackFuncType>(callback), std::forward<CallbackArgs>(callbackArgs)...); } template<typename CallbackFuncType, typename... CallbackArgs> static void bindTwice(CallbackFuncType&& callback, CallbackArgs&&... callbackArgs) { NoParameterBinding::bind(BindRequest::bindOnce<CallbackFuncType, CallbackArgs...>, std::forward<CallbackFuncType>(callback), std::forward<CallbackArgs>(callbackArgs)...); } }; void exampleCallback(const long& l, const double& d) { return; } int main() { NoParameterBinding::bind(exampleCallback,'a',9.9); SingleParameterBinding::bind(exampleCallback, 9.9); BindRequest::bindOnce(exampleCallback, 9.9); BindRequest::bindTwice(exampleCallback, 9.9); //Why freak out here? return 0; }
问题核心在于std::bind处理模板函数的机制:当你传递BindRequest::bindOnce<CallbackFuncType, CallbackArgs...>给std::bind时,传递的是模板函数实例化后的函数引用,但bindOnce的参数包含右值引用(CallbackFuncType&&、CallbackArgs&&...),std::bind在转发这些参数时会触发引用折叠问题,最终生成的绑定对象签名与std::function<void()>不匹配,导致转换失败。
另外,std::bind对模板函数、重载函数的处理本身容易引发陷阱,更推荐用lambda替代,写法直观且能规避引用相关问题。
修改BindRequest::bindTwice的实现,用lambda封装bindOnce的调用:
template<typename CallbackFuncType, typename... CallbackArgs> static void bindTwice(CallbackFuncType&& callback, CallbackArgs&&... callbackArgs) { NoParameterBinding::bind( [ cb = std::forward<CallbackFuncType>(callback), ... args = std::forward<CallbackArgs>(callbackArgs) ]() mutable { BindRequest::bindOnce(std::move(cb), std::move(args)...); } ); }
这个方案会创建一个无参lambda,内部调用bindOnce并传递绑定好的参数,lambda可以被正确转换为std::function<void()>,符合NoParameterBinding::Task的要求。
内容的提问来源于stack exchange,提问作者jonthalpy

