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

如何编写支持模板参数自动推导的旋转参数调用函数模板?

实现支持自动模板参数推导的call_rotated函数

问题背景

需要编写一个函数模板,调用目标函数时将传入参数按旋转顺序传递:把最后一个参数作为目标函数的第一个参数。现有实现能完成功能,但必须手动指定PARAMETERS模板参数(如call_rotated<int, int>),希望实现无需手动指定、支持自动推导的版本。

原实现的问题分析

原代码的模板参数推导失败,核心原因是C++不支持将非末尾的参数包自动推导:

template <typename ...PARAMETERS, typename LAST, typename FN>
void call_rotated(FN fn, PARAMETERS &&...parameters, LAST &&last) {
    fn(std::forward<LAST>(last), std::forward<PARAMETERS>(parameters)...);
}

这里PARAMETERS...位于函数参数列表的中间(FN fn之后,LAST &&last之前),编译器无法确定哪些参数属于PARAMETERS...,哪些属于LAST,因此必须手动指定模板参数。

解决方案:支持自动推导的实现

我们可以利用C++17及以上的特性,通过两种方式实现自动推导,代码如下:

方案1:基于std::tuple和std::apply(简洁直观)

#include <utility>
#include <tuple>
#include <cstdio>

template <typename FN, typename... Args>
void call_rotated(FN fn, Args&&... args) {
    // 处理无参数的边界情况
    if constexpr (sizeof...(Args) == 0) {
        fn();
        return;
    }

    // 将所有参数打包为tuple
    auto args_tuple = std::make_tuple(std::forward<Args>(args)...);
    
    // 用std::apply展开tuple,lambda自动匹配前N-1个参数和最后一个参数
    std::apply(
        [&fn](auto&&... front_args, auto&& last_arg) {
            fn(std::forward<decltype(last_arg)>(last_arg), 
               std::forward<decltype(front_args)>(front_args)...);
        },
        args_tuple
    );
}

// 测试用函数
void print(const char *l, int a, int b) {
    printf("%s %d %d\n", l, a, b);
}

int main() {
    // 无需手动指定模板参数,编译器自动推导
    call_rotated(&print, 1, 2, "was_last");
}

原理:

  • 把所有传入参数打包成std::tuple,此时参数包Args...位于函数参数列表末尾,编译器可以自动推导。
  • 通过std::apply将tuple展开,lambda的参数列表中,front_args...会匹配tuple中除最后一个外的所有元素,last_arg匹配最后一个元素,最终按要求顺序调用目标函数。

方案2:基于std::index_sequence(更底层的实现)

#include <utility>
#include <tuple>
#include <cstdio>

namespace detail {
    // 内部实现函数:利用索引序列拆分参数
    template <typename FN, typename... Args, std::size_t... Idx>
    void call_rotated_impl(FN fn, std::index_sequence<Idx...>, std::tuple<Args...>&& args) {
        // 取出最后一个参数,再依次取出前N-1个参数
        fn(std::get<sizeof...(Args)-1>(std::move(args)), 
           std::get<Idx>(std::move(args))...);
    }
}

template <typename FN, typename... Args>
void call_rotated(FN fn, Args&&... args) {
    if constexpr (sizeof...(Args) == 0) {
        fn();
        return;
    }

    // 生成前N-1个参数的索引序列(0到sizeof...(Args)-2)
    using Indices = std::make_index_sequence<sizeof...(Args)-1>;
    detail::call_rotated_impl(fn, Indices{}, 
                              std::make_tuple(std::forward<Args>(args)...));
}

// 测试用函数和main函数同方案1

原理:

  • 通过std::make_index_sequence生成前N-1个参数的索引,将参数打包为tuple后,按索引取出对应元素,最后一个元素通过固定索引sizeof...(Args)-1获取,再按要求顺序调用目标函数。

总结

两种方案都解决了原实现的手动指定模板参数问题,编译器可以自动推导FN和Args...模板参数,完全满足需求。推荐方案1,代码更简洁易读,依赖C++17及以上标准。

内容的提问来源于stack exchange,提问作者geza

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 01:10:00