如何用模板实现支持可变参数个数的sequence_id函数?
解决方案
方案1:利用std::array + 折叠表达式(C++17及以上)
这个方案代码简洁直观,完全避免了std::get变量索引的问题,同时无需编写多个重载函数:
#include <tuple> #include <array> #include <cassert> #include <unordered_map> typedef std::tuple<int, int, int, int> sequence_t; std::unordered_map<sequence_t, int> m_sequence_id; // 假设这是你的映射表 template<typename... Args, std::enable_if_t<(sizeof...(Args) >= 2 && sizeof...(Args) <= 4), bool> = true> int sequence_id(Args... args) { // 编译期校验参数类型必须是整数 static_assert((std::is_integral_v<std::decay_t<Args>> && ...), "sequence_id only accepts integer arguments"); // 初始化数组,默认填充-1 std::array<int, 4> seq_arr = {-1, -1, -1, -1}; int idx = 0; // 用折叠表达式依次填入参数,同时运行时校验参数为正整数 ((seq_arr[idx++] = args, assert(args > 0)), ...); // 转换为sequence_t并查询映射 sequence_t seq = std::make_tuple(seq_arr[0], seq_arr[1], seq_arr[2], seq_arr[3]); auto it = m_sequence_id.find(seq); return it != m_sequence_id.end() ? it->second : -1; }
方案2:利用编译期索引序列(C++17及以上)
如果你更倾向于纯编译期构造tuple,也可以用std::index_sequence生成编译期常量索引,避免运行时数组操作:
#include <tuple> #include <utility> #include <cassert> #include <unordered_map> typedef std::tuple<int, int, int, int> sequence_t; std::unordered_map<sequence_t, int> m_sequence_id; // 辅助模板:编译期构造补全后的sequence_t template<std::size_t... Is, typename... Args> sequence_t make_full_sequence(std::index_sequence<Is...>, Args&&... args) { constexpr std::size_t arg_count = sizeof...(Args); // 每个索引位置:如果小于参数个数则取对应参数,否则填-1 return sequence_t( (Is < arg_count ? std::get<Is>(std::forward_as_tuple(std::forward<Args>(args)...)) : -1)... ); } template<typename... Args, std::enable_if_t<(sizeof...(Args) >= 2 && sizeof...(Args) <= 4), bool> = true> int sequence_id(Args... args) { static_assert((std::is_integral_v<std::decay_t<Args>> && ...), "sequence_id only accepts integer arguments"); ((assert(args > 0), void()), ...); // 运行时校验正整数 // 生成0-3的编译期索引序列,构造完整tuple sequence_t seq = make_full_sequence(std::make_index_sequence<4>{}, std::forward<Args>(args)...); auto it = m_sequence_id.find(seq); return it != m_sequence_id.end() ? it->second : -1; }
关键优势
- 无需维护多个重载函数,一个模板函数覆盖2-4个参数的所有情况
- 编译期完成参数数量和类型校验,运行时仅需处理参数赋值和映射查询,效率与重载方案一致
- 彻底解决
std::get变量索引导致的编译错误问题,两种方案分别通过运行时数组赋值或编译期常量索引实现
内容的提问来源于stack exchange,提问作者E.S.
相关产品推荐
相关产品推荐

