C++:使用成员函数指针调用时如何省略默认参数?
问题描述
我实现了一个接收成员函数指针及对应参数的函数,用来在类实例上调用该成员函数。想问下能不能不用传递默认参数,或者通过某种方式获取默认参数的值?(忽略static实例)
代码示例
class X { public: void func(int p = 10); }; static X x; template <typename... Args> void call(void (X::* f)(Args...), Args... args) { // do something (x.*f)(args...); // do something } void main() { call(&X::func); //无法编译 call(&X::func, 10); //必须知晓默认参数才能调用 }
我尝试用宏来替代上述实现:
#define CALL(...) \ { \ // some setup \ __VA_ARGS__ \ // some cleanup \ } static X x; void main() { CALL(x.func();) }
解决方案
核心结论:C++标准不支持直接通过成员函数指针获取默认参数值
默认参数是编译期特性,只会在函数调用点被编译器自动填充,并不会被存储在函数指针或类的元数据中。所以没法通过成员函数指针直接拿到默认参数的值,这也是call(&X::func)编译失败的原因——模板推导时无法确定Args...的具体内容,编译器不知道要填充默认参数。
可行替代方案
用宏包裹调用(你已尝试的方法)
宏是预处理阶段的文本替换,能完整保留x.func()的调用形式,编译器处理宏展开后的代码时,会自动识别并填充默认参数。可以把宏写得更严谨,避免语法问题:#define CALL(code) do { \ // some setup \ code; \ // some cleanup \ } while(0)do-while(0)的包裹能避免宏在if等语句中出现语法错误,比如忘记加括号的情况。手动重载模板函数
若不想用宏,可针对带默认参数的成员函数手动编写模板重载:// 对应func的默认参数版本 void call(void (X::*f)(int)) { // do something (x.*f)(10); // 手动传入默认值 // do something } // 通用参数版本 template <typename... Args> void call(void (X::*f)(Args...), Args... args) { // do something (x.*f)(args...); // do something }缺点是每个带默认参数的成员函数都要写对应重载,扩展性差,且硬编码的默认值会和类定义产生冗余。
结合std::invoke与lambda(C++11及以上)
若能接受传递包装后的调用逻辑,用lambda结合std::invoke可以保留默认参数的调用能力,还更类型安全:#include <functional> template <typename Func, typename... Args> void call(Func&& func, Args... args) { // do something std::invoke(std::forward<Func>(func), args...); // do something } void main() { call([&x](){ x.func(); }); // lambda内直接调用,编译器自动填充默认参数 }
内容的提问来源于stack exchange,提问作者SMMB
相关产品推荐
相关产品推荐

