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

能否通过传入函数名到模板方法,自动推导并绑定同名重载函数指针到类成员?

能否通过传入函数名到模板方法,自动推导并绑定同名重载函数指针到类成员?

当然可以实现!不过直接写f.assign<Foo1>()这种方式一开始会碰壁——因为Foo1是重载函数,编译器没法确定你指的是哪个具体的重载版本,没法直接把它作为模板参数传递。不过我们可以借助C的SFINAE特性和if constexpr(C17及以上),或者C++20的auto模板参数来搞定这个需求。

核心思路

我们需要逐个检查Foo类里的目标类型(int、double、float)是否存在对应的Foo1重载,存在就把函数指针赋值给成员,不存在就设为nullptr。

完整实现代码(C++20版本,最简洁)

首先保留你原有的类型定义和Foo1重载,然后添加类型检测工具和修改assign方法:

#include <type_traits>

template <typename T>
using ConversionFunction = T(*)(T val);

static int Foo1(int x)
{
    return x * x;
}

static double Foo1(double x)
{
    return x * x + 1;
}

// 辅助类型trait:检测是否存在对应类型的Foo1重载
template <typename T, typename = void>
struct has_Foo1 : std::false_type {};

template <typename T>
struct has_Foo1<T, std::void_t<decltype(static_cast<ConversionFunction<T>>(&Foo1))>> : std::true_type {};

template <typename T>
constexpr bool has_Foo1_v = has_Foo1<T>::value;

struct Foo
{
    ConversionFunction<double> d = nullptr;
    ConversionFunction<int> i = nullptr;
    ConversionFunction<float> f = nullptr;

    // C++20支持auto&作为模板参数,直接接收重载函数名
    template <auto& Func>
    void assign(){
        // 处理double类型
        if constexpr (has_Foo1_v<double>) {
            d = static_cast<ConversionFunction<double>>(&Func);
        } else {
            d = nullptr;
        }
        // 处理int类型
        if constexpr (has_Foo1_v<int>) {
            i = static_cast<ConversionFunction<int>>(&Func);
        } else {
            i = nullptr;
        }
        // 处理float类型:因为没有Foo1(float)重载,所以会设为nullptr
        if constexpr (has_Foo1_v<float>) {
            f = static_cast<ConversionFunction<float>>(&Func);
        } else {
            f = nullptr;
        }
    }
};

// 使用示例
int main() {
    Foo f;
    f.assign<Foo1>();

    // 测试:调用int版本的Foo1
    if (f.i != nullptr) {
        printf("i: %d\n", f.i(2)); // 输出4
    }
    // 测试:调用double版本的Foo1
    if (f.d != nullptr) {
        printf("d: %lf\n", f.d(3.0)); // 输出10.000000
    }
    // float版本是nullptr,不会执行
    if (f.f != nullptr) {
        printf("f: %f\n", f.f(1.5f));
    }
    return 0;
}

关键细节解释

  1. 类型检测trait:has_Foo1和has_Foo1_v用来判断,对于类型T,是否能把Foo1强制转换为ConversionFunction<T>类型的函数指针——如果能,说明存在对应的重载。
  2. C++20的auto模板参数:template <auto& Func>让编译器自动推导传入的Foo1重载集,然后我们再逐个匹配目标类型。
  3. if constexpr:在编译时就判断是否存在对应重载,避免运行时的开销,同时也能避免不存在重载时的编译错误。

如果用C++17(不支持auto模板参数)怎么办?

如果你的编译器不支持C++20,可以用一个辅助结构体来封装重载集,然后把这个结构体作为模板参数传递:

// 封装Foo1重载的辅助结构体
struct Foo1Wrapper {
    template <typename T>
    auto operator()(T val) const -> decltype(Foo1(val)) {
        return Foo1(val);
    }
};

// 修改Foo的assign方法
struct Foo
{
    // ... 成员变量不变 ...

    template <typename Wrapper>
    void assign(){
        // 检测double版本是否存在
        if constexpr (std::is_invocable_r_v<double, Wrapper, double>) {
            d = [](double val) { return Wrapper{}(val); };
        } else {
            d = nullptr;
        }
        // 检测int版本是否存在
        if constexpr (std::is_invocable_r_v<int, Wrapper, int>) {
            i = [](int val) { return Wrapper{}(val); };
        } else {
            i = nullptr;
        }
        // 检测float版本是否存在
        if constexpr (std::is_invocable_r_v<float, Wrapper, float>) {
            f = [](float val) { return Wrapper{}(val); };
        } else {
            f = nullptr;
        }
    }
};

// 使用时这样调用
Foo f;
f.assign<Foo1Wrapper>();

这种方式用lambda把重载函数包装成符合ConversionFunction<T>类型的函数指针,效果是一样的,只是多了一个辅助结构体。

总结

不管用C17还是C20,都能实现你想要的“自动绑定同名重载函数指针”的需求。核心就是让编译器逐个检测目标类型对应的重载是否存在,然后针对性赋值。

备注:内容来源于stack exchange,提问作者Martin Perry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 14:09:54