最稳妥的委托起点是std::function+std::bind,因其能统一接纳各类可调用体、解耦目标对象并支持类型擦除,而裸成员函数指针必须绑定实例且无法直接用于算法或容器。

为什么 std::function + std::bind 是最稳妥的委托起点
直接用成员函数指针(void (T::*)())调用必须绑死对象实例,没法当“可调用体”传给算法或容器——这不是语法限制,是类型系统设计使然。委托的本质是「把调用延迟+解耦目标对象」,而裸指针做不到这点。
实操建议:
- 优先用
std::function<void></void>存储委托,它能统一接纳 lambda、绑定后的成员函数、普通函数 - 绑定时用
std::bind(&T::func, obj, std::placeholders::_1),注意obj是值拷贝还是引用:传&obj要确保对象生命周期足够长 - 避免在 lambda 捕获
this后又把 lambda 存到全局容器里——如果this对象提前析构,调用就崩成EXC_BAD_ACCESS或静默 UB
成员函数指针调用失败的三个典型错误现象
不是语法写不对,而是上下文错位:
- 报错
error: must use '.*' or '->*' to call pointer-to-member function:忘了用.*(对象)或->*(指针)操作符,直接写了obj.pmf() - 运行时崩溃,堆栈显示
__cxa_pure_virtual:用基类指针调用虚成员函数指针,但实际对象是派生类且该函数未重写或已被析构 - 值传递后调用正常,但换成引用/指针传参就出错:成员函数指针类型和对象类型必须严格匹配,
A::func的指针不能赋给B::func即使 B 继承 A 且 func 同名
std::mem_fn 和 std::bind 的关键区别在哪
std::mem_fn 不做绑定,只做“适配”;std::bind 才真正固化参数和对象。这是很多人混淆的根源。
立即学习“C++免费学习笔记(深入)”;
-
std::mem_fn(&T::func)返回一个可调用对象,但每次调用都得显式传入对象实例:fn(obj)或fn(&obj) -
std::bind(&T::func, obj)直接返回一个“已绑定对象”的新可调用体,后续调用无需再传 obj - 性能上,
std::mem_fn零开销(通常内联),std::bind在 C++11/14 中可能产生额外拷贝,C++17 后多数实现优化为 move-only - 兼容性:
std::mem_fn支持 const 成员函数指针自动推导 const 限定,std::bind不会——你得手动写&const T::func
想零开销?用模板 + 可变参数完美转发代替运行时绑定
如果你控制调用链全程(比如事件分发器内部),模板方案比 std::function 更快、更安全,也避开 RTTI 和 heap 分配。
- 核心是把委托类型作为模板参数传入:
template<typename f> void connect(F&& f)</typename>,内部用std::forward<f>(f)(args...)</f> - 调用点必须知道所有参数类型和数量,无法像
std::function那样抹平接口差异 - 不能存储到同质容器(如
std::vector<:function>></:function>),因为每个F是不同类型 - 容易踩坑:lambda 捕获了局部变量,却通过模板传进长期存活的对象里——编译器不会报错,但运行时访问野指针
成员函数指针本身不难,难的是在对象生命周期、类型擦除、性能约束之间找平衡点。别试图用一个方案覆盖所有场景,先明确委托要存多久、谁负责销毁、是否跨线程——这些决定了你该用 std::function、模板,还是干脆手写一个带弱引用的包装器。










