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

如何在编译时自动判断成员函数的参数类型是否为void

解决方案:自动推导成员函数参数类型的包装器实现

核心思路是通过模板元编程提取成员函数的参数信息,编译期自动判断目标函数是无参还是单参,从而省去手动传递ArgType的步骤,同时避免分支编译冲突。

完整实现代码

#include <cstring>
#include <utility>
#include <tuple>
#include <type_traits>

using namespace std;

// 成员函数特性提取器:解析成员函数指针的参数个数与类型
template<typename Func>
struct MemberFuncTraits;

// 特化:无参成员函数
template<typename Ret, typename Class>
struct MemberFuncTraits<Ret(Class::*)()> {
    static constexpr size_t ArgCount = 0;
    using ReturnType = Ret;
};

// 特化:单参成员函数
template<typename Ret, typename Class, typename Arg>
struct MemberFuncTraits<Ret(Class::*)(Arg)> {
    static constexpr size_t ArgCount = 1;
    using ReturnType = Ret;
    using ArgType = Arg;
};

// 辅助模板:简化参数个数的获取
template<typename Func>
constexpr size_t MemberFuncArgCount = MemberFuncTraits<Func>::ArgCount;

template <typename FunctionWrapperType>
struct THE_PROXY {
    THE_PROXY(FunctionWrapperType* wrapper) : wrapper_(wrapper) {}

    template<typename T>
    THE_PROXY& operator=(T val) {
        // 编译期判断目标函数的参数个数,仅实例化符合条件的分支
        if constexpr (MemberFuncArgCount<typename FunctionWrapperType::FuncType> == 0) {
            void* address_to_write_to = wrapper_->Set();
            memcpy(address_to_write_to, &val, sizeof(val));
        } else {
            wrapper_->Set(val);
        }
        return *this;
    }

private:
    FunctionWrapperType* wrapper_;
};

template<typename Function, typename ContainingClass>
struct FunctionWrapper {
    using FuncType = Function;
    using Traits = MemberFuncTraits<Function>;

    FunctionWrapper(Function func, ContainingClass* c) : func_(func), containingClass_(c) {}

    THE_PROXY<FunctionWrapper> operator*() { return THE_PROXY(this); }

private:
    template<class T> friend struct THE_PROXY;

    // 无参场景:调用返回void*的成员函数
    void* Set() requires (Traits::ArgCount == 0) {
        return std::invoke(func_, containingClass_);
    }

    // 单参场景:调用带参数的成员函数,添加类型匹配检查
    template<typename Arg>
    void Set(Arg arg) requires (Traits::ArgCount == 1) {
        static_assert(std::is_convertible_v<Arg, typename Traits::ArgType>, "参数类型不匹配");
        std::invoke(func_, containingClass_, arg);
    }

    Function func_;
    ContainingClass* containingClass_;
};

// 辅助函数:自动推导模板参数,简化包装器创建
template<typename Function, typename ContainingClass>
auto make_function_wrapper(Function func, ContainingClass* c) {
    return FunctionWrapper<Function, ContainingClass>(func, c);
}

// 测试用例
struct MyStruct2 {
    void* Func() { return &n_; } 
    void Func2(int n) { n_ = n; }  
    // 特殊场景:返回void*但带参数的函数
    void* Func3(int n) { n_ = n; return &n_; }
private:
    int n_;
};

int main() {
    MyStruct2 ms;

    // 自动推导模板参数,无需手动指定ArgType
    auto fw = make_function_wrapper(&MyStruct2::Func, &ms);
    auto fw2 = make_function_wrapper(&MyStruct2::Func2, &ms);
    auto fw3 = make_function_wrapper(&MyStruct2::Func3, &ms);

    *fw = 100;    // 走memcpy写入逻辑(无参函数)
    *fw2 = 65;    // 直接传参更新(单参函数)
    *fw3 = 200;   // 直接传参更新(虽返回void*但带参数)
}

关键细节说明

  1. 成员函数特性提取
    通过模板特化解析成员函数指针的签名,精准获取参数个数,完全不依赖返回值判断,解决了"返回void*但带参数"的特殊场景问题。

  2. 编译期分支控制
    使用constexpr结合参数个数判断,确保只有符合条件的代码分支被编译,彻底避免了原代码中"invoke找不到匹配函数"的编译错误。

  3. 编译期约束
    给Set方法添加requires约束,进一步确保只有对应参数个数的Set方法会被实例化,强化类型安全。

  4. 简化创建流程
    新增make_function_wrapper辅助函数,自动推导模板参数,无需手动编写冗长的模板参数列表。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 06:24:31