能否将泛型函数作为模板参数?C++17技术实现问询
泛型函数作为模板参数的C++17实现方案
你提到的第一种写法不可行,原因确实是模板参数包必须是最后一个模板参数,函数指针非类型参数不能放在参数包前面。第二种写法里,函数形参fn不具备外部链接属性,无法作为模板非类型参数传递给SomeTemplate——这是C++标准对模板非类型参数的硬性要求:必须是编译期可见的常量表达式,且具有外部/内部链接。
以下是几种C++17下的可行实现方式:
1. 调整模板参数顺序(针对非模板函数)
把函数指针非类型参数移到模板参数包之后,满足参数包必须为最后一项的要求:
#include <type_traits> // 示例目标模板 template <auto Fn> struct SomeTemplate { void execute() {} }; // 调整后的method模板 template <typename Return, typename... Args, Return (*Fn)(Args...)> void method() { static_assert(std::is_function_v<decltype(Fn)>); SomeTemplate<Fn> instance; instance.execute(); } // 测试用普通函数 int add(int a, int b) { return a + b; } // 调用方式 int main() { method<int, int, int, &add>(); return 0; }
这种写法需要显式指定返回值、参数类型和函数地址,略显繁琐,但完全符合C++17标准。
2. 使用C++17的auto非类型模板参数(推荐)
C++17引入的auto非类型模板参数可以自动推导函数指针的类型,大幅简化代码:
#include <type_traits> template <auto Fn> struct SomeTemplate { void execute() {} }; template <auto Fn> void method() { static_assert(std::is_function_v<decltype(Fn)>); SomeTemplate<Fn> instance; instance.execute(); } int add(int a, int b) { return a + b; } int main() { method<&add>(); // 编译器自动推导Fn的完整类型 return 0; }
这种写法无需手动指定返回值和参数类型,是C++17下最简洁的方案。
3. 处理泛型模板函数
如果要传递的是模板函数(比如泛型identity函数),需要先显式实例化模板函数,再传递其实例的地址——模板函数本身不是具体的函数实体,只有实例化后才会生成具有链接属性的函数对象:
#include <type_traits> template <auto Fn> struct SomeTemplate { void execute() {} }; template <auto Fn> void method() { static_assert(std::is_function_v<decltype(Fn)>); SomeTemplate<Fn> instance; instance.execute(); } // 泛型模板函数 template <typename T> T identity(T val) { return val; } int main() { method<&identity<int>>(); // 显式实例化int版本 method<&identity<double>>(); // 显式实例化double版本 return 0; }
4. 用仿函数(函数对象)实现泛型逻辑
如果需要更灵活的泛型行为,可以将逻辑封装为带模板operator()的仿函数,作为模板类型参数传递——这种方式无需依赖函数指针的链接属性,天然支持泛型:
// 泛型仿函数 struct Identity { template <typename T> T operator()(T val) const { return val; } }; // 接受类型参数的目标模板 template <typename Fn> struct SomeTemplate { void execute() { Fn fn; auto res = fn(42); // 调用仿函数的泛型operator() } }; // 直接传递仿函数类型 void method() { SomeTemplate<Identity> instance; instance.execute(); }
关键说明
你之前的第二种写法无法工作的核心原因:函数形参fn是运行时的局部变量(即使传入的是编译期常量地址),不具备模板非类型参数要求的编译期可见的链接属性,因此不能直接用于SomeTemplate<fn>。必须将函数地址作为模板参数传递,而非函数形参。
内容的提问来源于stack exchange,提问作者Criminal_Affair_At_SO
相关产品推荐
相关产品推荐

