如何从函数类型推导C++类模板的模板参数?
实现类模板
action_的自动参数推导 要实现action_的模板参数自动推导,让第二个参数T根据第一个参数Func的参数类型自动匹配,我们可以借助C++20的**类模板参数推导(CTAD)**结合自定义类型特性(Trait)来完成,核心是建立函数参数类型到T的映射关系。
步骤1:提取函数的第一个参数类型
首先定义一个Trait,用于从函数指针/引用类型中提取第一个参数的类型,这是推导T的基础:
#include <type_traits> // 用于std::decay_t // 辅助Trait:提取可调用对象的第一个参数类型 template<typename F> struct first_arg_of; // 特化:处理函数指针 template<typename R, typename Arg> struct first_arg_of<R(*)(Arg)> { using type = Arg; }; // 特化:处理函数引用 template<typename R, typename Arg> struct first_arg_of<R(&)(Arg)> { using type = Arg; }; // 别名模板简化使用 template<typename F> using first_arg_of_t = typename first_arg_of<std::decay_t<F>>::type;
步骤2:反向映射参数类型到T
根据现有Viewer的特化规则,定义反向映射Trait,将参数类型(如std::string_view&)对应到目标T(如char):
// 辅助Trait:反向映射Viewer类型到对应的T template<typename V> struct reverse_viewer; // 特化:std::string_view(含引用)对应char template<> struct reverse_viewer<std::string_view> { using type = char; }; template<> struct reverse_viewer<std::string_view&> { using type = char; }; // 特化:std::span<Token>(含引用)对应Token template<> struct reverse_viewer<std::span<Token>> { using type = Token; }; template<> struct reverse_viewer<std::span<Token>&> { using type = Token; }; // 别名模板简化使用 template<typename V> using reverse_viewer_t = typename reverse_viewer<V>::type;
步骤3:添加类模板推导指南
利用C++20的CTAD特性,为action_定义模板推导指南,让编译器自动推导第二个参数T:
// 类模板推导指南:从Func自动推导T template<typename Func> action_(Func&&) -> action_<Func, reverse_viewer_t<first_arg_of_t<Func>>>;
修改后的完整代码
#include <span> #include <string_view> #include <vector> #include <type_traits> struct Token {}; template <typename T> struct Viewer; // explicit specialization for T = Token template <> struct Viewer<Token> { using type = std::span<Token>; }; // explicit specialization for T = char template <> struct Viewer<char> { using type = std::string_view; }; // alias template template <typename T> using ViewerT = typename Viewer<T>::type; template <typename Base, typename T> struct parser_base { using v = ViewerT<T>; using charType = T; }; // 辅助Trait:提取可调用对象的第一个参数类型 template<typename F> struct first_arg_of; template<typename R, typename Arg> struct first_arg_of<R(*)(Arg)> { using type = Arg; }; template<typename R, typename Arg> struct first_arg_of<R(&)(Arg)> { using type = Arg; }; template<typename F> using first_arg_of_t = typename first_arg_of<std::decay_t<F>>::type; // 辅助Trait:反向映射Viewer类型到对应的T template<typename V> struct reverse_viewer; template<> struct reverse_viewer<std::string_view> { using type = char; }; template<> struct reverse_viewer<std::string_view&> { using type = char; }; template<> struct reverse_viewer<std::span<Token>> { using type = Token; }; template<> struct reverse_viewer<std::span<Token>&> { using type = Token; }; template<typename V> using reverse_viewer_t = typename reverse_viewer<V>::type; template <typename Func, typename T> struct action_ final : public parser_base<action_<Func, T>, T> { constexpr explicit action_(Func&& func) noexcept : func(std::forward<Func>(func)) {} template<typename V, typename Skipper> constexpr inline bool visit(V& sv, Skipper& skipper) const& noexcept { return func(sv); } private: Func func; }; // 类模板推导指南 template<typename Func> action_(Func&&) -> action_<Func, reverse_viewer_t<first_arg_of_t<Func>>>; using myTestAction = bool(std::string_view& sv); constexpr bool testAction(std::string_view& sv) { return true; } // 测试Token场景的函数 constexpr bool testTokenAction(std::span<Token>& tokens) { return true; } int main() { auto p1 = action_<myTestAction*, char>(testAction); // 自动推导char场景 auto p2 = action_(testAction); static_assert(std::is_same_v<decltype(p2), action_<bool(&)(std::string_view&), char>>); // 自动推导Token场景 auto p3 = action_(testTokenAction); static_assert(std::is_same_v<decltype(p3), action_<bool(&)(std::span<Token>&), Token>>); return 0; }
核心说明
first_arg_of负责从函数指针/引用中提取第一个参数类型,std::decay_t统一处理函数名(左值引用)和函数指针的差异。reverse_viewer实现了参数类型到T的反向映射,与原Viewer的特化规则完全对应。- 推导指南告诉编译器:当用
Func类型的参数构造action_时,第二个模板参数T由reverse_viewer_t<first_arg_of_t<Func>>自动推导得出。
内容的提问来源于stack exchange,提问作者ollydbg
相关产品推荐
相关产品推荐

