You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

functionWrapper类无法正确传递引用参数问题求助

问题分析与修复

你的functionWrapper无法正确传递引用参数,核心问题有两个:

  1. 类型转换错误:调用operator()时,模板参数Args...是从实参推导而来的。比如传入左值a,会被推导为int而非int&,这导致你尝试将实际存储的B<int&>*强制转换为B<int>*,属于未定义行为,最终函数接收的是参数拷贝,原变量不会被修改。
  2. 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;
}

关键修改说明

  1. 新增多态调用接口:在基类A中添加纯虚函数call(std::any),让每个B子类实现该接口,避免调用时的错误类型转换,通过多态保证调用正确的函数实例。
  2. 用std::tuple+std::apply传递参数:将调用时的参数打包为std::tuple,通过std::any传递给B的call方法,再用std::apply解包tuple并调用目标函数,确保引用参数正确传递。
  3. 完善function_traits特化:补充了const成员函数指针的特化,适配lambda表达式的情况。
  4. 修正std::forward用法:对每个参数单独使用std::forward<Args>(args),保证参数的引用类型正确传递。

运行修复后的代码,输出会符合预期:

5
6

内容的提问来源于stack exchange,提问作者widejaw78

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 08:03:16