C++中如何不依赖标准库从参数包移除指定索引元素
编译期移除参数包指定索引元素的无依赖实现
本方案完全不依赖任何C++标准库头文件,可直接运行在内核模式等无法使用标准库的场景,要求待移除的元素索引为编译期可确定的constexpr整数值。
核心实现思路
整个逻辑完全在编译期完成,运行时无额外开销:
- 自实现最小化编译期索引序列工具,替代标准库
integer_sequence,用来承载编译期整数集合 - 实现编译期索引过滤逻辑:从0开始的连续参数索引中,剔除所有标记为待移除的索引,生成需要保留的索引序列
- 利用参数地址顺序和参数包展开顺序一致的特性,按保留索引从入参中取出对应元素,直接转发给目标函数
完整实现代码
// -------------- 基础编译期工具,无任何标准库依赖 -------------- // 编译期索引序列类型 template<size_t... Idx> struct idx_seq {}; // 生成0~N-1连续索引的元函数 template<size_t N, size_t... Tail> struct make_idx_seq_impl : make_idx_seq_impl<N-1, N-1, Tail...> {}; template<size_t... Tail> struct make_idx_seq_impl<0, Tail...> { using type = idx_seq<Tail...>; }; template<size_t N> using make_idx_seq = typename make_idx_seq_impl<N>::type; // 基础类型 traits 工具 template<typename T> struct remove_ref { using type = T; }; template<typename T> struct remove_ref<T&> { using type = T; }; template<typename T> struct remove_ref<T&&> { using type = T; }; template<typename T> constexpr T&& forward(typename remove_ref<T>::type& arg) noexcept { return static_cast<T&&>(arg); } template<typename T> constexpr T&& forward(typename remove_ref<T>::type&& arg) noexcept { return static_cast<T&&>(arg); } // 编译期判断值是否在待移除索引集合中 template<size_t V, size_t... RemoveIdx> struct is_in_remove_set; template<size_t V> struct is_in_remove_set<V> { static constexpr bool value = false; }; template<size_t V, size_t First, size_t... Rest> struct is_in_remove_set<V, First, Rest...> { static constexpr bool value = (V == First) || is_in_remove_set<V, Rest...>::value; }; // 索引过滤逻辑 template<typename CurrentSeq, bool Keep, size_t CurIdx, size_t... RemoveIdx> struct filter_idx_impl; template<size_t... Kept, size_t CurIdx, size_t... RemoveIdx> struct filter_idx_impl<idx_seq<Kept...>, true, CurIdx, RemoveIdx...> { using type = idx_seq<Kept..., CurIdx>; }; template<size_t... Kept, size_t CurIdx, size_t... RemoveIdx> struct filter_idx_impl<idx_seq<Kept...>, false, CurIdx, RemoveIdx...> { using type = idx_seq<Kept...>; }; template<typename InputSeq, size_t... RemoveIdx> struct filter_seq; template<size_t First, size_t... Rest, size_t... RemoveIdx> struct filter_seq<idx_seq<First, Rest...>, RemoveIdx...> { private: static constexpr bool keep = !is_in_remove_set<First, RemoveIdx...>::value; using prev = typename filter_seq<idx_seq<Rest...>, RemoveIdx...>::type; public: using type = typename filter_idx_impl<prev, keep, First, RemoveIdx...>::type; }; template<size_t... RemoveIdx> struct filter_seq<idx_seq<>, RemoveIdx...> { using type = idx_seq<>; }; // -------------- 业务逻辑部分,对应示例场景 -------------- template<typename... Args> void foo1(Args... arguments) { // 原有foo1业务逻辑 } template<size_t... KeepIdx, typename... Args> void call_foo1_filtered(idx_seq<KeepIdx...>, Args&&... arguments) { // 按保留索引取对应参数转发给foo1,运行时仅做一次指针数组初始化,开销可忽略 auto arg_ptr_arr = {&arguments...}; foo1(forward<Args>(*static_cast<typename remove_ref<Args>::type*>(arg_ptr_arr[KeepIdx]))...); } template<typename... Args> void foo2(Args... arguments) { // 这里插入原有逻辑,编译期确定需要移除的索引值 constexpr size_t arg_count = sizeof...(Args); using full_indexes = make_idx_seq<arg_count>; // 模板参数里填入所有要移除的constexpr索引,比如要移除第0、2位参数就写0,2 using kept_indexes = typename filter_seq<full_indexes, /* 待移除索引填在此处 */0, 2>::type; // 调用转发函数,传入过滤后的参数 call_foo1_filtered(kept_indexes{}, forward<Args>(arguments)...); }
使用说明
- 索引计数从0开始,和参数包展开顺序完全对应
- 支持任意数量的待移除索引,只要是编译期
constexpr size_t类型的值,都可以直接作为模板参数传入filter_seq - 所有元计算逻辑在编译阶段完成,生成的汇编代码和手动写过滤后传参的逻辑完全一致,没有运行时性能损耗
- 全程未引入任何标准库头文件,所有工具都是独立实现,兼容内核态开发要求
内容的提问来源于stack exchange,提问作者SimpleY
相关产品推荐
相关产品推荐

