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

C++17及更早版本下能否按约束用可变参数函数/模板传递可变栈参数?

结论

场景A(匹配目标函数可接收的参数数量)可以实现,场景B(由用户自定义整数指定次数)仅支持编译期常量的次数,运行时动态次数无法实现,因为C++是静态类型语言,函数参数的数量、类型都必须在编译期确定,无法根据运行时的数值动态生成参数列表。
以下实现完全满足你的三个约束:

  • 不依赖任何STL、Boost或第三方库,仅用C++核心语法
  • 无运行时循环、递归,参数展开完全在编译期完成
  • 兼容C14及以上标准,自然满足C17要求

实现方案

首先你原有代码中使用float(...)类型的可变函数指针无法提取参数数量,我们需要基于目标函数的实际类型,通过编译期索引序列展开对应次数的pop_t调用。

1. 自定义编译期索引序列(无STL依赖)

我们自己实现编译期整数序列生成逻辑,不依赖STL的std::index_sequence:

template <size_t... Is> struct index_sequence {};
template <size_t N, size_t... Is> struct make_index_sequence_impl : make_index_sequence_impl<N-1, N-1, Is...> {};
template <size_t... Is> struct make_index_sequence_impl<0, Is...> { using type = index_sequence<Is...>; };
template <size_t N> using make_index_sequence = typename make_index_sequence_impl<N>::type;

2. 实现参数展开逻辑

通过模板提取函数参数个数,生成对应长度的索引序列,展开为N次pop_t调用:

// 你的pop_t实现,注意修正原有未定义行为
template<class U, class I>
U pop_t(U* stack, I &stackidx, I &stacksize) {
    if(stacksize>0) {
        stacksize--;
        stackidx--; // 原写法stackidx = stackidx--属于未定义行为,已修正
        return stack[stackidx];
    }
    return stack[stackidx];
}

// 核心展开逻辑:根据索引序列长度生成N次pop_t调用
template <class U, class I, class Func, size_t... Is>
auto invoke_with_n_pops(Func func, U* stack, I& stackidx, I& stacksize, index_sequence<Is...>) {
    return func(pop_t(stack, stackidx, stacksize)...);
}

// 场景A实现:自动匹配目标函数的参数数量
template <class Ret, class... Args, class U, class I>
auto invoke_with_stack(Ret (*func)(Args...), U* stack, I& stackidx, I& stacksize) {
    return invoke_with_n_pops(func, stack, stackidx, stacksize, make_index_sequence<sizeof...(Args)>{});
}

// 场景B实现:编译期指定调用次数N
template <size_t N, class Func, class U, class I>
auto invoke_with_stack_n(Func func, U* stack, I& stackidx, I& stacksize) {
    return invoke_with_n_pops(func, stack, stackidx, stacksize, make_index_sequence<N>{});
}

3. 使用示例

#include <cmath> // 仅用于示例获取sin、atan2函数指针,不属于约束禁止的STL使用

int main() {
    float outputstack[2] = {3.1415926f/2, 1.0f};
    int o_stackidx = 2;
    int o_stacksize = 2;

    // 场景A用法:自动识别函数参数数量调用
    float atan2_val = invoke_with_stack(&atan2, outputstack, o_stackidx, o_stacksize);
    // 等价于:atan2(pop_t(outputstack, o_stackidx, o_stacksize), pop_t(outputstack, o_stackidx, o_stacksize))

    o_stackidx = 1; o_stacksize = 1;
    float sin_val = invoke_with_stack(&sin, outputstack, o_stackidx, o_stacksize);
    // 等价于:sin(pop_t(outputstack, o_stackidx, o_stacksize))

    // 场景B用法:编译期指定调用次数为2
    o_stackidx = 2; o_stacksize = 2;
    float custom_val = invoke_with_stack_n<2>((float(*)(float,float))&atan2, outputstack, o_stackidx, o_stacksize);

    return 0;
}

注意事项

  1. 不要将函数指针强制转为float(...)类型的可变参数函数指针再调用,类型不匹配会触发未定义行为,必须保证调用时的函数类型和实际目标函数类型完全一致。
  2. 场景B仅支持编译期常量的次数,运行时传入的动态次数无法实现,因为编译器无法在编译阶段确定需要生成多少个参数。
  3. 所有模板展开都在编译期完成,没有运行时循环或递归开销,也没有用到任何STL的容器、算法组件,完全符合约束要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 19:09:02