You need to enable JavaScript to run this app. C++中如何从成员函数指针提取类型参数包实现通用invoke函数
优惠活动
大模型
产品
解决方案
定价
更多

C++中如何从成员函数指针提取类型参数包实现通用invoke函数

你这个可变参的实现思路完全可行,只要修正两个核心问题就能实现零冗余的通用invoke

你现在写的代码跑不起来,本质是踩了C++模板的两个常见坑:

  • 参数包不能用typedef存储。C++规范里可变参数包不能通过typedef做中转存储,你写的typedef Args Arguments根本没法让编译器识别出后续展开的是参数列表,写typename FunctionPointer<Func>::Arguments ... params的时候会直接报语法错。要留存参数列表,要么用std::tuple封装参数包,要么在偏特化场景下直接透传参数包类型。
  • 模板推导的非推导上下文问题。你定义的invoke模板里,Func是嵌套依赖类型FunctionPointer<Func>的模板参数,属于C++标准规定的非推导上下文——编译器在推导模板参数时,不会去解析依赖类型的嵌套成员反推模板参数值。哪怕语法全对,只要调用时不手动显式指定invoke<decltype(&BaseClass::print2)>(...),编译器根本不知道Func该匹配成什么类型,直接报模板匹配失败。

修正后的实现不需要为每种参数数量写单独代码

核心调整两个点:

  1. 把成员函数指针的类型作为独立的、可直接推导的模板参数,不要把它藏在嵌套依赖类型里
  2. 从成员函数指针的偏特化结构里直接萃取类类型、参数类型,形参直接对接萃取结果,自然触发隐式转换逻辑

下面是可以直接跑的实现,完全匹配你的调用需求:

#include <iostream>
#include <type_traits>
#include <utility>

// 成员函数指针萃取结构:匹配普通无修饰的成员函数
template <auto Func>
struct MemFuncTraits;

// 偏特化:拆出所属类、返回值、参数列表
template <class Class, typename Ret, typename... Args>
struct MemFuncTraits<Ret (Class::*)(Args...)> {
    using TargetClass = Class;
    using RetType = Ret;

    static Ret call(TargetClass* obj, Args... params) {
        return (obj->*Func)(params...);
    }
};

// 通用invoke实现
template <auto Func, class ObjType, typename... CallArgs>
auto invoke(ObjType&& obj, CallArgs&&... args) {
    using Traits = MemFuncTraits<Func>;
    // 静态检查:对象类型必须和成员函数所属类兼容
    static_assert(
        std::is_base_of_v<typename Traits::TargetClass, std::decay_t<ObjType>>,
        "传入对象和成员函数所属类不兼容"
    );
    // 赋值给基类指针时自动触发子类向基类的隐式向上转型
    typename Traits::TargetClass* base_ptr = &obj;
    // 参数匹配成员函数要求的类型,自动触发int转float这类隐式转换
    return Traits::call(base_ptr, std::forward<CallArgs>(args)...);
}

// 测试用例
class BaseClass {
public:
    virtual void print2(float arg1, float arg2) = 0;
    virtual ~BaseClass() = default;
};

class SubClass : public BaseClass {
public:
    void print2(float arg1, float arg2) override {
        std::cout << "调用成功,参数值:" << arg1 << ", " << arg2 << std::endl;
    }
};

int main() {
    SubClass subclass;
    // 完全符合你要求的调用形式
    invoke<&BaseClass::print2>(subclass, 1, 2);
}

实现效果完全匹配你的三个要求

  • 传入的子类对象会在赋值给基类指针时自动完成向上转型,不需要手动写强制转换
  • 传入的int类型实参,会自动匹配成员函数要求的float形参,触发隐式类型转换
  • 只要传入的对象和成员函数所属类不兼容、参数无法完成隐式转换,就会直接触发静态断言报错或者类型不匹配的编译错误,不会等到运行时出问题

如果你不想用非类型模板参数传成员函数指针,也可以把Func作为独立的可推导参数写,调用时连模板参数都不用手动传:

// 调整后的萃取和invoke
template <class Func> struct FunctionPointer;
template <class Class, typename... Args>
struct FunctionPointer<void (Class::*)(Args...)> {
    using ClassType = Class;
};

template <typename Func, class ObjType, typename... CallArgs>
void invoke(Func func, ObjType&& obj, CallArgs&&... args) {
    using Traits = FunctionPointer<Func>;
    typename Traits::ClassType* base_ptr = &obj;
    (base_ptr->*func)(std::forward<CallArgs>(args)...);
}

// 调用时直接写,完全自动推导
invoke(&BaseClass::print2, subclass, 1, 2);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.02 19:36:10