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

如何从函数类型推导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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 18:47:19