能否通过传入函数名到模板方法,自动推导并绑定同名重载函数指针到类成员?
能否通过传入函数名到模板方法,自动推导并绑定同名重载函数指针到类成员?
当然可以实现!不过直接写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; }
关键细节解释
- 类型检测trait:
has_Foo1和has_Foo1_v用来判断,对于类型T,是否能把Foo1强制转换为ConversionFunction<T>类型的函数指针——如果能,说明存在对应的重载。 - C++20的auto模板参数:
template <auto& Func>让编译器自动推导传入的Foo1重载集,然后我们再逐个匹配目标类型。 - 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
相关产品推荐
相关产品推荐

