如何在数组元组的对应元素上调用可变参数模板函数以实现向量版函数
这是个很典型的可变参数模板结合tuple展开的问题,我来给你两种可行的实现方案,分别适配不同的C++版本:
方案一:C++17及以上(简洁版,用std::apply)
C++17引入的std::apply可以直接帮我们把tuple的元素展开成参数包,配合lambda捕获索引i就能轻松实现需求:
#include <array> #include <tuple> #include <functional> // 引入std::apply // 假设你的func_scalar已经实现 template<typename retScalar, typename... scalars> retScalar func_scalar(scalars... items); template<typename retScalar, typename... scalars, std::size_t LEN> std::array<retScalar, LEN> func_vec(const std::tuple<std::array<scalars, LEN>...>& vecs) { std::array<retScalar, LEN> res{}; // 值初始化,确保每个元素有默认值 for (std::size_t i = 0; i < LEN; ++i) { // 用std::apply把tuple展开为每个array的引用,lambda捕获i后取出对应位置元素 res[i] = std::apply([i](const auto&... arr) { return func_scalar<retScalar>(arr[i]...); }, vecs); } return res; }
解释:
std::apply会把传入的tuplevecs的所有元素(也就是各个std::array)展开,作为参数传给lambda;- lambda捕获当前循环的索引
i,对每个传入的array取第i个元素,再把这些元素展开成参数包传给func_scalar; - 显式指定
func_scalar<retScalar>是为了确保返回类型符合我们要生成的结果数组类型,避免编译器推导歧义。
方案二:C++14及以上(兼容旧版本,用索引序列)
如果你的项目还在使用C++14,我们可以用std::index_sequence来手动展开tuple的元素:
#include <array> #include <tuple> #include <utility> // 引入std::index_sequence和std::index_sequence_for template<typename retScalar, typename... scalars> retScalar func_scalar(scalars... items); // 辅助函数:通过索引序列展开tuple元素 namespace detail { template<typename retScalar, typename Tuple, std::size_t... Idx> retScalar call_func_scalar_at_idx(const Tuple& vecs, std::size_t i, std::index_sequence<Idx...>) { // 用std::get<Idx>取出tuple中第Idx个array,再取第i个元素,展开为参数包 return func_scalar<retScalar>(std::get<Idx>(vecs)[i]...); } } template<typename retScalar, typename... scalars, std::size_t LEN> std::array<retScalar, LEN> func_vec(const std::tuple<std::array<scalars, LEN>...>& vecs) { std::array<retScalar, LEN> res{}; for (std::size_t i = 0; i < LEN; ++i) { // 生成与scalars数量匹配的索引序列,传给辅助函数 res[i] = detail::call_func_scalar_at_idx<retScalar>( vecs, i, std::index_sequence_for<scalars...>{} ); } return res; }
解释:
std::index_sequence_for<scalars...>会生成一个包含0,1,...,N-1的索引序列(N是scalars的数量);- 辅助函数
call_func_scalar_at_idx利用这个索引序列,通过std::get<Idx>逐个取出tuple中的array,再提取第i个元素,最后展开成参数包调用func_scalar。
关键注意事项
- LEN作为模板参数:编译器会自动根据传入tuple中各个
std::array的大小推导LEN,确保所有输入array的长度一致(否则编译报错); - const引用传参:避免对大数组进行不必要的拷贝,提升性能;
- 结果初始化:用
res{}做值初始化,对于数值类型会默认设为0,避免未定义行为。
示例使用
假设func_scalar是一个求和函数,我们可以这样测试:
// 示例func_scalar实现:对所有参数求和(C++17折叠表达式) template<typename retScalar, typename... scalars> retScalar func_scalar(scalars... items) { return (items + ...); } int main() { std::array<int, 3> arr1 = {1, 2, 3}; std::array<int, 3> arr2 = {4, 5, 6}; std::array<int, 3> arr3 = {7, 8, 9}; auto result = func_vec<int>(std::make_tuple(arr1, arr2, arr3)); // result的值为 {12, 15, 18},对应每个索引位置的元素求和 return 0; }
内容的提问来源于stack exchange,提问作者Regis Portalez
相关产品推荐
相关产品推荐

