如何使用Lambda遍历std::tuple索引并操作多组元组?
多元组按索引遍历执行元素级运算的多版本C++解决方案
针对你需要遍历多个同结构元组的对应索引元素、执行统一运算的需求,以下是不同C++版本下的优雅实现方案:
C++20及以上(支持模板Lambda)
C++20的模板Lambda和折叠表达式让这个需求的实现最简洁,不需要复杂的辅助模板:
方案1:通用索引遍历辅助函数
直接生成元组的索引序列,用折叠表达式展开每个索引的操作:
#include <tuple> #include <utility> template <typename Func, typename... Tuples> void tuple_for_each_index(Func&& func, Tuples&&... tuples) { constexpr auto tuple_size = std::tuple_size_v<std::remove_reference_t<decltype(std::get<0>(std::forward_as_tuple(tuples...)))>>; // 用模板Lambda捕获索引序列 [&]<std::size_t... Indices>(std::index_sequence<Indices...>) { // 折叠表达式逐个调用函数 (func(std::get<Indices>(std::forward<Tuples>(tuples))...), ...); }(std::make_index_sequence<tuple_size>{}); }
使用示例(对应你的operator+=场景):
FSkyLightSettings& operator+=(FSkyLightSettings& Other) { auto tup1 = AsTuple(); auto tup2 = Other.AsTuple(); tuple_for_each_index([](auto& elem1, auto& elem2) { *elem1 += *elem2; // 直接利用元素的+=运算符,无需显式指定索引 }, tup1, tup2); return *this; }
如果需要显式获取当前索引(比如做额外逻辑),可以修改辅助函数传递索引常量:
template <typename Func, typename... Tuples> void tuple_for_each_index_with_idx(Func&& func, Tuples&&... tuples) { constexpr auto tuple_size = std::tuple_size_v<std::remove_reference_t<decltype(std::get<0>(std::forward_as_tuple(tuples...)))>>; [&]<std::size_t... Indices>(std::index_sequence<Indices...>) { (func(std::integral_constant<std::size_t, Indices>{}, std::get<Indices>(std::forward<Tuples>(tuples))...), ...); }(std::make_index_sequence<tuple_size>{}); }
使用时可以拿到索引值:
tuple_for_each_index_with_idx([](auto idx, auto& elem1, auto& elem2) { // idx.value() 即为当前元素的索引 *elem1 += *elem2; }, tup1, tup2);
方案2:修复你原有的代码逻辑
你之前尝试的递归Lambda遇到C3536错误,是因为Lambda未完成定义就递归调用自身。C++20里无需递归,直接用折叠表达式展开所有索引调用即可:
FSkyLightSettings& operator+=(FSkyLightSettings& Other) { auto tup1 = AsTuple(); auto tup2 = Other.AsTuple(); using TupType = decltype(tup1); auto add_func = []<typename Tup, int Index>(Tup t1, Tup t2) { *t1.template Get<Index>() += *t2.template Get<Index>(); }; // 生成索引序列并折叠调用 [&, add_func]<std::size_t... Indices>(std::index_sequence<Indices...>) { (add_func.template operator()<TupType, Indices>(tup1, tup2), ...); }(std::make_index_sequence<std::tuple_size_v<TupType>>{}); return *this; }
C++17版本(支持折叠表达式和std::index_sequence,无模板Lambda)
C++17没有模板Lambda,需要用普通函数模板替代:
通用辅助函数实现
#include <tuple> #include <utility> // 单索引操作函数 template <std::size_t Index, typename Func, typename... Tuples> void apply_single_index(Func&& func, Tuples&&... tuples) { func(std::get<Index>(std::forward<Tuples>(tuples))...); } // 索引序列展开实现 template <typename Func, typename... Tuples, std::size_t... Indices> void tuple_for_each_index_impl(Func&& func, std::index_sequence<Indices...>, Tuples&&... tuples) { (apply_single_index<Indices>(std::forward<Func>(func), std::forward<Tuples>(tuples)...), ...); } // 对外接口 template <typename Func, typename... Tuples> void tuple_for_each_index(Func&& func, Tuples&&... tuples) { constexpr auto tuple_size = std::tuple_size_v<std::remove_reference_t<decltype(std::get<0>(std::forward_as_tuple(tuples...)))>>; tuple_for_each_index_impl(std::forward<Func>(func), std::make_index_sequence<tuple_size>{}, std::forward<Tuples>(tuples)...); }
使用示例:
FSkyLightSettings& operator+=(FSkyLightSettings& Other) { auto tup1 = AsTuple(); auto tup2 = Other.AsTuple(); tuple_for_each_index([](auto& elem1, auto& elem2) { *elem1 += *elem2; }, tup1, tup2); return *this; }
若需要索引,修改apply_single_index传递索引常量即可:
template <std::size_t Index, typename Func, typename... Tuples> void apply_single_index(Func&& func, Tuples&&... tuples) { func(std::integral_constant<std::size_t, Index>{}, std::get<Index>(std::forward<Tuples>(tuples))...); }
C11/C14版本(无折叠表达式,需递归模板展开)
这两个版本没有折叠表达式,只能通过递归模板来遍历索引:
通用辅助函数实现
#include <tuple> #include <utility> // 递归终止条件:索引等于元组大小时停止 template <std::size_t Index, typename Func, typename... Tuples> typename std::enable_if_t<Index == std::tuple_size_v<std::remove_reference_t<decltype(std::get<0>(std::forward_as_tuple(tuples...)))>>> tuple_for_each_index_impl(Func&&, Tuples&&...) {} // 递归展开:处理当前索引后,递归处理下一个索引 template <std::size_t Index, typename Func, typename... Tuples> typename std::enable_if_t<Index < std::tuple_size_v<std::remove_reference_t<decltype(std::get<0>(std::forward_as_tuple(tuples...)))>>> tuple_for_each_index_impl(Func&& func, Tuples&&... tuples) { func(std::get<Index>(std::forward<Tuples>(tuples))...); tuple_for_each_index_impl<Index + 1>(std::forward<Func>(func), std::forward<Tuples>(tuples)...); } // 对外接口:从索引0开始递归 template <typename Func, typename... Tuples> void tuple_for_each_index(Func&& func, Tuples&&... tuples) { tuple_for_each_index_impl<0>(std::forward<Func>(func), std::forward<Tuples>(tuples)...); }
使用示例:
FSkyLightSettings& operator+=(FSkyLightSettings& Other) { auto tup1 = AsTuple(); auto tup2 = Other.AsTuple(); tuple_for_each_index([](auto& elem1, auto& elem2) { *elem1 += *elem2; }, tup1, tup2); return *this; }
需要索引的话,修改递归函数传递std::integral_constant即可。
内容的提问来源于stack exchange,提问作者Artem Selivanov
相关产品推荐
相关产品推荐

