非递归实现多源tuple元素赋值至预创建目标tuple的技术问题
解决方案:非递归实现多源Tuple元素赋值到预创建目标Tuple
要解决代码中???的替换问题,核心是编译时计算每个源Tuple在目标Tuple中的起始偏移量(即前面所有源Tuple的元素总数之和),然后通过折叠表达式展开赋值操作。以下是两种兼容不同C++版本的非递归实现方案:
方案1:C++20 简洁实现(推荐)
利用C++20的lambda模板参数特性,直接在函数内完成编译时偏移计算与赋值展开:
#include <tuple> #include <array> #include <utility> #include <cassert> template<std::size_t from_element, typename dstT, typename srcTs, std::size_t... Is> void assign_elms(dstT& dst, const srcTs& src, std::index_sequence<Is...>) { ((std::get<from_element + Is>(dst) = std::get<Is>(src)), ...); } template<std::size_t from_element, typename dstT, typename srcT> void assign_impl(dstT& dst, const srcT& src) { assign_elms<from_element>(dst, src, std::make_index_sequence<std::tuple_size_v<srcT>>()); } template<typename dstT, typename... srcTs> void assign(dstT& dst, const srcTs&... srcs) { // 编译时获取每个源Tuple的元素数量 constexpr std::array src_sizes = {std::tuple_size_v<srcTs>...}; // 编译时生成每个源Tuple的起始偏移量(前缀和) constexpr auto prefix_offsets = []{ std::array<std::size_t, sizeof...(srcTs)> offsets{}; std::size_t current_offset = 0; for(std::size_t i = 0; i < sizeof...(srcTs); ++i) { offsets[i] = current_offset; current_offset += src_sizes[i]; } return offsets; }(); // 绑定偏移量与对应源Tuple,展开赋值操作 auto apply_assignments = [&]<std::size_t... Is>(std::index_sequence<Is...>) { (assign_impl<prefix_offsets[Is]>(dst, std::get<Is>(std::tie(srcs...))), ...); }; apply_assignments(std::index_sequence_for<srcTs...>()); } template<typename... Ts> using tuple_concat_t = decltype(std::tuple_cat(std::declval<Ts>()...)); int main() { std::tuple t1{ 1, 2.2f, 3.33 }; std::tuple t2{ 4, 5.5f }; std::tuple t3{ 6 }; tuple_concat_t<decltype(t1), decltype(t2), decltype(t3)> tr; assign(tr, t1, t2, t3); // 验证赋值结果 static_assert(std::tuple_size_v<decltype(tr)> == 6); assert(std::get<0>(tr) == 1); assert(std::get<1>(tr) == 2.2f); assert(std::get<2>(tr) == 3.33); assert(std::get<3>(tr) == 4); assert(std::get<4>(tr) == 5.5f); assert(std::get<5>(tr) == 6); }
方案2:C++17 兼容实现
如果需要兼容C++17(不支持lambda模板参数),可以通过辅助模板函数完成展开:
#include <tuple> #include <array> #include <utility> #include <cassert> template<std::size_t from_element, typename dstT, typename srcTs, std::size_t... Is> void assign_elms(dstT& dst, const srcTs& src, std::index_sequence<Is...>) { ((std::get<from_element + Is>(dst) = std::get<Is>(src)), ...); } template<std::size_t from_element, typename dstT, typename srcT> void assign_impl(dstT& dst, const srcT& src) { assign_elms<from_element>(dst, src, std::make_index_sequence<std::tuple_size_v<srcT>>()); } // 辅助函数:绑定偏移量与源Tuple展开赋值 template<typename dstT, typename... srcTs, std::size_t... Is> void assign_helper(dstT& dst, const std::tuple<srcTs...>& src_tuple, std::index_sequence<Is...>, const std::array<std::size_t, sizeof...(srcTs)>& offsets) { (assign_impl<offsets[Is]>(dst, std::get<Is>(src_tuple)), ...); } template<typename dstT, typename... srcTs> void assign(dstT& dst, const srcTs&... srcs) { constexpr std::array src_sizes = {std::tuple_size_v<srcTs>...}; constexpr auto prefix_offsets = []{ std::array<std::size_t, sizeof...(srcTs)> offsets{}; std::size_t current_offset = 0; for(std::size_t i = 0; i < sizeof...(srcTs); ++i) { offsets[i] = current_offset; current_offset += src_sizes[i]; } return offsets; }(); assign_helper(dst, std::tie(srcs...), std::index_sequence_for<srcTs...>(), prefix_offsets); } template<typename... Ts> using tuple_concat_t = decltype(std::tuple_cat(std::declval<Ts>()...)); int main() { std::tuple t1{ 1, 2.2f, 3.33 }; std::tuple t2{ 4, 5.5f }; std::tuple t3{ 6 }; tuple_concat_t<decltype(t1), decltype(t2), decltype(t3)> tr; assign(tr, t1, t2, t3); // 验证赋值结果 static_assert(std::tuple_size_v<decltype(tr)> == 6); assert(std::get<0>(tr) == 1); assert(std::get<1>(tr) == 2.2f); assert(std::get<2>(tr) == 3.33); assert(std::get<3>(tr) == 4); assert(std::get<4>(tr) == 5.5f); assert(std::get<5>(tr) == 6); }
核心逻辑说明
- 编译时计算源Tuple大小:通过
std::tuple_size_v获取每个源Tuple的元素数量,存入编译时常量数组。 - 生成前缀和偏移量:用constexpr lambda计算每个源Tuple在目标Tuple中的起始位置(第一个源从0开始,后续源的起始位置为前面所有源的元素总数之和)。
- 展开赋值操作:利用
std::index_sequence_for生成源Tuple的索引序列,将每个源Tuple与对应的偏移量绑定,调用assign_impl完成元素赋值。
内容的提问来源于stack exchange,提问作者Konstantin Makarov
相关产品推荐
相关产品推荐

