如何避免递归,以类std::views方式对参数包过滤生成std::tuple?
参数包类型过滤的非递归实现方案
问题描述
我有时需要从给定的参数包中过滤某些类型,并将结果以std::tuple形式返回,例如可基于类型在参数包中的索引进行过滤。
以下是一个类步长过滤的递归实现示例,其语义与std::views::stride类似:
#include <tuple> namespace pack { template<int R, int IDX, typename...ARGS> struct stride_impl { }; template<int R, int IDX, typename...ARGS> using stride_impl_t = typename stride_impl<R,IDX,ARGS...>::type; template<int R, int IDX, typename T, typename...ARGS> struct stride_impl<R,IDX,T,ARGS...> { using type = std::conditional_t < IDX%R==0, decltype(std::tuple_cat ( std::tuple<T> {}, stride_impl_t<R,IDX+1,ARGS...> {} )), stride_impl_t<R,IDX+1,ARGS...> >; }; template<int R, int IDX> struct stride_impl<R,IDX> { using type = std::tuple<>; }; template<int R, typename...ARGS> struct stride { static_assert(R>0); using type = stride_impl_t<R,0,ARGS...>; }; template<int R, typename ...ARGS> using stride_t = typename stride<R,ARGS...>::type; }; template<int R> using example = pack::stride_t<R,char,int,long,float,double>; static_assert (std::is_same_v< example<1>, std::tuple<char,int,long,float,double>>); static_assert (std::is_same_v< example<2>, std::tuple<char, long, double>>); static_assert (std::is_same_v< example<3>, std::tuple<char, float >>); static_assert (std::is_same_v< example<4>, std::tuple<char, double>>); static_assert (std::is_same_v< example<5>, std::tuple<char >>); int main() {}
该递归实现较为繁琐,请问是否存在无需显式递归的更直接实现方式(例如使用折叠表达式)?或者标准库中是否有类似std::views的工具可用于此类参数包过滤?
解决方案
方法一:利用索引序列+折叠表达式实现
借助std::index_sequence生成参数包的索引,通过折叠表达式完成条件筛选与tuple拼接,完全避免显式递归:
#include <tuple> #include <utility> #include <type_traits> namespace pack { template<int R, typename... Args> using stride_t = decltype([]<std::size_t... Idx>(std::index_sequence<Idx...>) { return std::tuple_cat( // 对每个索引判断是否符合步长条件,符合则保留该类型的tuple,否则为空tuple std::declval<std::conditional_t<Idx % R == 0, std::tuple<Args>, std::tuple<>>>()... ); }(std::index_sequence_for<Args...>{})); } template<int R> using example = pack::stride_t<R, char, int, long, float, double>; // 验证结果正确性 static_assert(std::is_same_v<example<1>, std::tuple<char, int, long, float, double>>); static_assert(std::is_same_v<example<2>, std::tuple<char, long, double>>); static_assert(std::is_same_v<example<3>, std::tuple<char, float>>); static_assert(std::is_same_v<example<4>, std::tuple<char, double>>); static_assert(std::is_same_v<example<5>, std::tuple<char>>); int main() {}
这个方案的核心是:
- 用
std::index_sequence_for自动生成与参数包长度匹配的索引序列 - 通过C++20模板lambda捕获索引序列,结合折叠表达式一次性完成所有类型的条件判断与tuple拼接
方法二:简化版无lambda实现(兼容C++17)
如果需要兼容C++17,可以用辅助函数替代模板lambda,同样避免显式递归:
#include <tuple> #include <utility> #include <type_traits> namespace pack { template<int R, typename... Args, std::size_t... Idx> auto stride_helper(std::index_sequence<Idx...>) -> decltype(std::tuple_cat( std::declval<std::conditional_t<Idx % R == 0, std::tuple<Args>, std::tuple<>>>()... )); template<int R, typename... Args> using stride_t = decltype(stride_helper<R, Args...>(std::index_sequence_for<Args...>{})); } template<int R> using example = pack::stride_t<R, char, int, long, float, double>; // 验证同前 static_assert(std::is_same_v<example<1>, std::tuple<char, int, long, float, double>>); static_assert(std::is_same_v<example<2>, std::tuple<char, long, double>>); static_assert(std::is_same_v<example<3>, std::tuple<char, float>>); static_assert(std::is_same_v<example<4>, std::tuple<char, double>>); static_assert(std::is_same_v<example<5>, std::tuple<char>>); int main() {}
标准库工具说明
标准库中没有直接针对编译期参数包的过滤视图工具,std::views::stride仅适用于运行时范围。但C++17及以后的元编程特性(折叠表达式、std::index_sequence)已经能大幅简化这类编译期类型过滤的实现,无需依赖额外库。
内容的提问来源于stack exchange,提问作者abcdefg
相关产品推荐
相关产品推荐

