C++如何消重重载类成员函数并获取其首个参数的类型
解决方案
核心思路是利用SFINAE让编译器根据传入的实参类型,自动从重载集中筛选出匹配的成员函数,再提取对应的形参类型,完整可运行的实现如下:
#include <type_traits> class Abase { public: void fun2() { // Success! } }; class C {void fun2() {};}; class A1 : public Abase {}; class A2 : public Abase {}; class A : public A1, public A2 {}; class B { public: void fun1(A1* t) {} void fun1(C* t) {} }; // 辅助元工具:推导传入Arg类型参数时,Obj::fun1匹配的第一个形参类型 template <typename Obj, typename Arg> struct get_matched_fun1_param { private: // SFINAE筛选可以接受Arg类型转换的重载 template <typename Ret, typename ParamType> static typename std::enable_if< std::is_convertible<Arg, ParamType>::value, ParamType >::type test(Ret(Obj::* f)(ParamType)); public: using type = decltype(test(&Obj::fun1)); }; template <typename Obj, typename Arg> using get_matched_fun1_param_t = typename get_matched_fun1_param<Obj, Arg>::type; template <typename T1,typename T2> void problematic(T1* a, T2* b) { b->fun1(a); // 自动匹配重载得到参数类型完成转换 static_cast<get_matched_fun1_param_t<T2, T1*>>(a)->fun2(); } int main() { A a; B b; problematic(&a, &b); }
原理解释
- 直接取重载成员函数的地址会有歧义,因为编译器不知道需要选择哪个版本的重载,我们通过
std::is_convertible添加SFINAE约束,让编译器只保留能接受当前传入实参类型的重载。 - 当有且仅有一个重载满足转换要求时,
decltype就能正确推导得到对应形参的类型,和实际调用fun1时的重载决议结果完全一致。 - 如果存在多个满足转换要求的重载,会直接编译报错,和普通调用重载函数出现歧义的表现一致,符合预期。
如果需要适配任意名称的成员函数,可以通过宏封装上述逻辑,C++11暂不支持将函数名作为模板参数直接传递。
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

