如何用可变参数模板实现多任意类型std::vector的并排打印函数?
实现多vector并排打印的解决方案
你的问题核心是如何在同一作用域中访问所有传入的vector参数,以便按索引并行遍历——递归分离参数的方式确实做不到这一点,因为它每次只处理一个参数,没法同时拿到所有vector的同一索引元素。下面提供两种实用的实现方案,分别适配C17及以上,和C11/14版本。
方案一:C++17 折叠表达式(简洁首选)
C++17引入的折叠表达式可以直接展开参数包,结合tuple来统一管理所有vector,这样就能在循环的每个索引步中访问所有参数的对应元素。
#include <iostream> #include <vector> #include <tuple> template <typename... Ts> void printV(const std::vector<Ts>&... vecs) { // 处理空调用的边界情况 if constexpr (sizeof...(vecs) == 0) { return; } // 以第一个vector的长度为基准(你假设所有vector长度一致) const auto total_rows = std::get<0>(std::make_tuple(vecs...)).size(); for (std::size_t i = 0; i < total_rows; ++i) { std::cout << i << ": "; // 先打印第一个元素,避免开头出现多余逗号 std::cout << std::get<0>(std::make_tuple(vecs...)).at(i); // 用折叠表达式遍历剩余所有vector,打印逗号+元素 ((std::cout << ", " << vecs.at(i)), ...); std::cout << '\n'; } }
代码说明:
std::make_tuple(vecs...)把所有传入的vector打包成一个tuple,方便统一访问;if constexpr是C++17的编译期条件判断,避免空调用时出错;- 折叠表达式
((std::cout << ", " << vecs.at(i)), ...)会自动展开为:std::cout << ", " << vec1.at(i); std::cout << ", " << vec2.at(i); ... std::cout << ", " << vecN.at(i);
方案二:C++11/14 适配版(无折叠表达式)
如果你的环境不支持C17,可以用tuple+索引序列的方式模拟参数包展开,这是C11标准下处理可变参数的经典技巧。
#include <iostream> #include <vector> #include <tuple> #include <utility> // 用于std::index_sequence namespace detail { // 辅助函数:打印单行的所有vector元素 template <typename Tuple, std::size_t... Is> void print_single_row(std::size_t row_idx, const Tuple& vec_tuple, std::index_sequence<Is...>) { std::cout << row_idx << ": "; bool is_first_element = true; // 用"吞掉数组"技巧展开参数包(C++11无折叠表达式的替代方案) using swallow = int[]; (void)swallow{0, ( [&]() { if (!is_first_element) { std::cout << ", "; } std::cout << std::get<Is>(vec_tuple).at(row_idx); is_first_element = false; }(), 0)...}; std::cout << '\n'; } // 辅助函数:遍历所有行,调用单行打印 template <typename Tuple> void print_all_rows(const Tuple& vec_tuple) { const auto total_rows = std::get<0>(vec_tuple).size(); // 生成索引序列:0,1,...,N-1(N是vector的数量) using indices = std::make_index_sequence<std::tuple_size<Tuple>::value>; for (std::size_t i = 0; i < total_rows; ++i) { print_single_row(i, vec_tuple, indices{}); } } } // 对外暴露的接口函数 template <typename... Ts> void printV(const std::vector<Ts>&... vecs) { if (sizeof...(vecs) == 0) { return; } // 将所有vector打包成tuple,传递给内部函数处理 auto vec_tuple = std::make_tuple(vecs...); detail::print_all_rows(vec_tuple); }
代码说明:
std::index_sequence会生成一个编译期的整数序列,对应tuple中每个vector的索引;- "吞掉数组"技巧
(void)swallow{0, (... ,0)}是C++11中用来强制展开参数包的常用方法,确保每个vector的元素都会被处理;
测试示例
不管用哪个方案,你都可以像这样调用:
int main() { std::vector<int> v1 = {1, 2, 3}; std::vector<double> v2 = {1.5, 2.5, 3.5}; std::vector<std::string> v3 = {"foo", "bar", "baz"}; printV(v1, v2, v3); }
输出结果:
0: 1, 1.5, foo 1: 2, 2.5, bar 2: 3, 3.5, baz
扩展提示
如果需要处理vector长度不一致的情况,可以修改代码取所有vector中最小的长度作为循环上限,或者对超出长度的vector打印占位符(比如"N/A"),只需要在获取total_rows时做一下计算:
// C++17版本示例:取最小长度 const auto total_rows = std::min({vecs.size()...});
内容的提问来源于stack exchange,提问作者duskusus
相关产品推荐
相关产品推荐

