疑问3
疑问
请问 std::bind(
&zkos::diag::sdm::SdmIpcRuntime::RecvMethodCall,
&sdm_runtime,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3
)中的参数是固定格式吗?
解答
std::bind 里的参数不是固定格式,但必须遵循「bind 的参数规则 + 被绑定函数的签名匹配规则」——你示例中的参数格式,是针对 RecvMethodCall 这个类成员函数的适配写法,换个函数(比如全局函数、参数个数不同的函数),格式会立刻变化。
下面我会拆解「规则」和「变体」,让你彻底明白怎么灵活写,而不是死记格式:
一、先明确 std::bind 的核心规则(决定参数格式)
std::bind 的作用是「把函数+参数(部分/全部)绑定成一个新的可调用对象」,参数格式由两个核心因素决定:
- 被绑定的函数类型(类成员函数/全局函数/lambda);
- 被绑定函数的参数列表(个数、类型、顺序);
- 是否要“占位”(预留参数位置,让后续调用时传入)。
二、你的示例:针对「类成员函数」的绑定格式(最常见)
先回顾你的场景:
- 被绑定函数:
SdmIpcRuntime::RecvMethodCall(类非静态成员函数); - 函数签名:
bool RecvMethodCall(uint32_t, std::string, Message::UniquePtr&&); - 目标:绑定后生成
DoRequestCallBack类型(std::function<bool(uint32_t, std::string, Message::UniquePtr&&)>)。
你的绑定格式拆解(规则对应):
std::bind(
&zkos::diag::sdm::SdmIpcRuntime::RecvMethodCall, // 1. 类成员函数的地址(必须加&,必须写全类名)
&sdm_runtime, // 2. 类实例的指针(成员函数必须绑定实例,否则无法调用)
std::placeholders::_1, // 3. 占位符:对应函数第一个参数(uint32_t methodId)
std::placeholders::_2, // 4. 占位符:对应函数第二个参数(std::string strRequest)
std::placeholders::_3 // 5. 占位符:对应函数第三个参数(Message::UniquePtr&&)
)
核心规则(类成员函数绑定):
| 位置 | 必选/可选 | 规则 |
|---|---|---|
| 第一个参数 | 必选 | 类成员函数的地址(格式:&类名::函数名) |
| 第二个参数 | 必选 | 类实例的指针(&对象)或引用(std::ref(对象)) |
| 后续参数 | 可选 | 要么是「占位符(_1/_2/_3...)」,要么是「固定值」;个数=函数参数个数 |
三、格式不是固定的!看3个变体案例(对比理解)
变体1:绑定「全局函数」(无需传实例)
如果 RecvMethodCall 是全局函数:
// 全局函数
bool GlobalRecvMethodCall(uint32_t methodId, std::string strRequest, Message::UniquePtr&& message) {
// 逻辑...
}
// bind格式(无类实例参数)
std::bind(
&GlobalRecvMethodCall, // 全局函数地址(可省略&,但建议加)
std::placeholders::_1, // 占位符1
std::placeholders::_2, // 占位符2
std::placeholders::_3 // 占位符3
)
→ 少了「类实例指针」这个参数,因为全局函数不属于任何对象。
变体2:固定部分参数(不需要占位)
如果想固定 methodId 为 0x1001(只处理读故障码),只预留后两个参数:
std::bind(
&SdmIpcRuntime::RecvMethodCall,
&sdm_runtime,
0x1001, // 固定值:methodId永远是0x1001
std::placeholders::_1, // 占位符对应原函数第二个参数(strRequest)
std::placeholders::_2 // 占位符对应原函数第三个参数(message)
)
→ 后续调用 handler_(strRequest, message) 即可,methodId 会自动填 0x1001。
变体3:函数参数个数不同(比如只有2个参数)
如果 RecvMethodCall 简化为:
bool RecvMethodCall(uint32_t methodId, std::string strRequest) {
// 逻辑...
}
// bind格式(只有2个占位符)
std::bind(
&SdmIpcRuntime::RecvMethodCall,
&sdm_runtime,
std::placeholders::_1, // 对应methodId
std::placeholders::_2 // 对应strRequest
)
→ 占位符个数=函数参数个数,顺序一一对应。
四、关键易错点(格式错了会编译失败)
- 类成员函数必须传实例:
漏写&sdm_runtime会编译报错——成员函数需要“对象+函数地址”才能调用(就像你要调用“张三的吃饭方法”,必须先指定“张三”这个对象)。 - 占位符顺序必须匹配函数参数顺序:
_1对应函数第一个参数,_2对应第二个,不能乱序(比如把_2对应第一个参数会编译失败/逻辑错误)。 - 占位符个数 ≤ 函数参数个数:
可以少(固定部分参数),但不能多(比如函数只有3个参数,用了_4会编译报错)。 - 右值引用参数不影响bind格式:
你示例中的Message::UniquePtr&&是右值引用,但bind只关心“参数个数/顺序”,不关心“是否是引用”——_3依然能正确绑定。
五、总结:怎么写bind参数(通用步骤)
- 第一步:确定被绑定函数的类型(类成员/全局/lambda);
- 类成员函数:先写
&类名::函数名,再写&对象; - 全局/静态函数:只写
&函数名(可省略&);
- 类成员函数:先写
- 第二步:数被绑定函数的参数个数,按顺序写「占位符/_N」或「固定值」;
- 想后续传参:用
_1/_2/_3...(从1开始); - 想固定参数:直接写值(如
0x1001);
- 想后续传参:用
- 第三步:确保最终绑定后的对象,能匹配目标类型(比如你的
DoRequestCallBack)。
回到你的问题:示例中的参数格式是“针对RecvMethodCall这个类成员函数+3个参数全占位”的写法,不是固定格式——换函数、换参数、换绑定方式,格式都会变,但遵循上面的规则就不会错。
浙公网安备 33010602011771号