基于可变参数模板的成员函数指针包装(GCC、Clang)及编译报错问题
为什么这段代码无法在GCC或Clang中编译?
咱们一步一步拆解问题,看看这段代码里的两个关键错误:
1. 模板参数包的位置违反标准规则
你的模板声明是这样的:
template< class T, typename ...Args, void(T::*Member)(Args...) >
这里的typename ...Args是一个模板参数包,但它不是模板参数列表的最后一项,后面还跟着成员函数指针参数void(T::*Member)(Args...)。C++标准明确要求,普通函数模板的模板参数包必须放在参数列表的最后位置——否则编译器根本不知道该如何推导或匹配后面的参数,这就像你把可变长度的箱子放在固定大小的箱子前面,快递员没法分清哪个是哪个。
2. 函数参数列表里的可变参数位置错误
再看函数定义:
void member_dispatch(Args&&... args, void* userdata) { ... }
这里的Args&&... args是展开后的可变参数,但它被放在了void* userdata前面。C++同样规定,函数的可变参数包必须位于参数列表的末尾,不然编译器无法判断调用时哪些参数属于可变参数包,哪些属于后面的固定参数。比如你调用时传了1, &foo,编译器根本搞不清&foo是属于args还是单独的userdata。
修正后的代码示例
我们调整一下模板参数和函数参数的顺序,就能解决问题:
#include <utility> class Foo { public: void bar(int) {} }; // 把成员函数指针参数移到模板参数包前面,让编译器能通过它推导Args template< class T, void(T::*Member)(auto...), typename ...Args > void member_dispatch(void* userdata, Args&&... args) { T* obj = static_cast<T*>(userdata); (obj->*Member)(std::forward<Args>(args)...); } int main() { Foo foo; // 现在只需要指定T和Member,Args会被自动推导 member_dispatch<Foo, &Foo::bar>(&foo, 1); return 0; }
这里的核心调整有两个:
- 将成员函数指针模板参数移到
Args...前面,这样编译器可以通过&Foo::bar的类型自动推断出Args的类型(这里就是int)。 - 把
void* userdata移到函数参数列表的最前面,让可变参数包Args&&... args留在末尾,符合C++的参数匹配规则。
内容的提问来源于stack exchange,提问作者zse
相关产品推荐
相关产品推荐

