C++模板传入虚成员函数时如何实现非虚直接调用
问题根因
你遇到的是C++的标准行为:通过虚成员函数指针调用函数时,默认会触发虚机制走虚表查找,实现多态调用。这是因为取&Test::func得到的虚成员函数指针,本质存储的是虚表的槽位偏移而非函数直接地址,调用时会自动走虚表查找流程。
而你写a->Test::func()时,显式指定了Test::作用域,编译器会跳过虚表查找,直接调用Test类的func实现,也就是你想要的非虚调用。
解决方案
要让模板内的调用也走非虚逻辑,只需要在调用时显式指定所属类的作用域,强制编译器跳过虚表查找即可。
首先通过类型萃取工具,从成员函数指针中提取它所属的类类型:
// 类型萃取:从成员函数指针中提取所属类 template <typename T> struct member_func_traits; template <typename Ret, typename Class, typename... Args> struct member_func_traits<Ret (Class::*)(Args...)> { using class_type = Class; using return_type = Ret; }; // 可根据需要补充const、volatile、引用限定版本的特化
然后修改foo模板,调用时加上类作用域限定:
template <auto F, typename T> void foo(T* instance) { using Class = typename member_func_traits<decltype(F)>::class_type; // 显式加Class::限定,强制非虚调用 (static_cast<Class*>(instance)->Class::*F)(); }
完整测试代码
#include <cstdio> class Test { public: virtual void func() { printf("foo\n"); } }; // 类型萃取 template <typename T> struct member_func_traits; template <typename Ret, typename Class, typename... Args> struct member_func_traits<Ret (Class::*)(Args...)> { using class_type = Class; }; template <auto F, typename T> void foo(T* instance) { using Class = typename member_func_traits<decltype(F)>::class_type; (static_cast<Class*>(instance)->Class::*F)(); } int main() { auto a = new Test; a->Test::func(); // 直接非虚调用 auto f = &Test::func; (a->*f)(); // 虚调用 foo<&Test::func>(a); // 现在是和a->Test::func()完全一致的非虚调用 }
如果你使用C++20及以上版本,也可以直接传入包装了非虚调用的无捕获lambda作为模板参数,写法更灵活:
template <auto Callable, typename T> void foo(T* instance) { Callable(instance); } // 调用时直接包装非虚逻辑 foo<[](Test* t) { t->Test::func(); }>(a);
内容的提问来源于stack exchange,提问作者mat
相关产品推荐
相关产品推荐

