functionWrapper类无法正确传递引用参数问题求助
问题分析与修复
你的functionWrapper无法正确传递引用参数,核心问题有两个:
- 类型转换错误:调用
operator()时,模板参数Args...是从实参推导而来的。比如传入左值a,会被推导为int而非int&,这导致你尝试将实际存储的B<int&>*强制转换为B<int>*,属于未定义行为,最终函数接收的是参数拷贝,原变量不会被修改。 std::forward用法错误:原代码中std::forward<Args...>(args...)的写法不正确,应该对每个参数单独使用std::forward<Args>(args)...。
修复后的完整代码
#include <iostream> #include <functional> #include <tuple> #include <type_traits> #include <any> template <typename T> struct function_traits; // 函数指针特化 template <typename Ret, typename... Args> struct function_traits<Ret(*)(Args...)> { using return_type = Ret; using args_type = std::tuple<Args...>; }; // std::function特化 template <typename Ret, typename... Args> struct function_traits<std::function<Ret(Args...)>> { using return_type = Ret; using args_type = std::tuple<Args...>; }; // 成员函数指针特化(普通版) template <typename Ret, typename Class, typename... Args> struct function_traits<Ret(Class::*)(Args...)> { using return_type = Ret; using args_type = std::tuple<Args...>; }; // 成员函数指针特化(const版,适配lambda) template <typename Ret, typename Class, typename... Args> struct function_traits<Ret(Class::*)(Args...) const> { using return_type = Ret; using args_type = std::tuple<Args...>; }; // lambda/仿函数适配 template <typename T> struct function_traits : function_traits<decltype(&T::operator())> {}; class functionWrapper { template <typename... Args> class B; class A { public: template <typename F> static auto castToB(F func) { using ArgsTuple = typename function_traits<F>::args_type; return castToBImpl(std::make_index_sequence<std::tuple_size<ArgsTuple>::value>{}, func); } virtual ~A(){} // 新增纯虚调用接口,统一处理参数传递 virtual void call(std::any args_tuple) = 0; private: template <std::size_t... Is, typename F> static auto castToBImpl(std::index_sequence<Is...>, F func) { return new B<typename std::tuple_element<Is, typename function_traits<F>::args_type>::type...>(func); } }; template <typename... Args> class B : public A { public: std::function<void(Args...)> function; template <typename F> B(F f) : function(f) {} virtual ~B(){} // 实现基类的call接口,解包tuple并调用函数 void call(std::any args_tuple) override { auto& tuple = std::any_cast<std::tuple<Args...>&>(args_tuple); std::apply(function, tuple); } }; private: A* ptr = nullptr; public: template <typename F> functionWrapper(F f) { ptr = static_cast<A*>(A::castToB(f)); } functionWrapper(){} virtual ~functionWrapper(){ delete ptr; } template <typename... Args> void operator()(Args&&... args) { if (ptr) { // 将参数打包为tuple,通过多态传递给正确的B实例 auto args_tuple = std::make_tuple(std::forward<Args>(args)...); ptr->call(args_tuple); } } }; // 测试代码 void foo(int& a){ std::cout << a++ << '\n'; } int main() { int a = 5; functionWrapper wrap{foo}; wrap(a); std::cout << a << '\n'; return 0; }
关键修改说明
- 新增多态调用接口:在基类
A中添加纯虚函数call(std::any),让每个B子类实现该接口,避免调用时的错误类型转换,通过多态保证调用正确的函数实例。 - 用
std::tuple+std::apply传递参数:将调用时的参数打包为std::tuple,通过std::any传递给B的call方法,再用std::apply解包tuple并调用目标函数,确保引用参数正确传递。 - 完善
function_traits特化:补充了const成员函数指针的特化,适配lambda表达式的情况。 - 修正
std::forward用法:对每个参数单独使用std::forward<Args>(args),保证参数的引用类型正确传递。
运行修复后的代码,输出会符合预期:
5 6
内容的提问来源于stack exchange,提问作者widejaw78
相关产品推荐
相关产品推荐

