如何优雅地从含成对/元组的范围填充多范围?C++解包方案问询
C++范围的“解包”实现索引筛选函数的优化方案
C++标准库目前没有原生提供直接“解包”范围(将成对/元组组成的范围拆分为多个独立范围)的工具,但我们可以通过一些简洁的技巧避免重复代码,实现和原函数等价的功能。
优化思路:生成配对视图后一次性拆分
首先,通过std::views::transform将索引映射为顶点和法线的配对视图,再借助自定义小工具一次性将配对拆分到两个容器中,避免重复调用std::views::elements:
#include <ranges> #include <vector> #include <utility> // 自定义解包配对范围的工具函数 template <typename Range, typename T1, typename T2> void unpack_pairs(Range&& range, std::vector<T1>& dest1, std::vector<T2>& dest2) { const auto size = std::ranges::size(range); dest1.reserve(size); dest2.reserve(size); for (auto&& [vert, norm] : std::forward<Range>(range)) { dest1.push_back(std::forward<decltype(vert)>(vert)); dest2.push_back(std::forward<decltype(norm)>(norm)); } } struct Point3D { float x, y, z; }; auto getVerticesAndNormalsForIndices(const std::vector<int>& indices, const std::vector<Point3D>& allVertices, const std::vector<Point3D>& allNormals) { // 生成由顶点-法线对组成的视图 auto paired_view = indices | std::views::transform([&](int idx) { return std::pair{allVertices[idx], allNormals[idx]}; }); std::vector<Point3D> vertices, normals; unpack_pairs(paired_view, vertices, normals); return std::pair{std::move(vertices), std::move(normals)}; }
更简洁的直接遍历写法
如果不需要依赖视图特性,直接遍历索引填充两个容器的写法反而更简洁,完全规避了中间配对的生成:
struct Point3D { float x, y, z; }; auto getVerticesAndNormalsForIndices(const std::vector<int>& indices, const std::vector<Point3D>& allVertices, const std::vector<Point3D>& allNormals) { std::vector<Point3D> vertices, normals; vertices.reserve(indices.size()); normals.reserve(indices.size()); for (int idx : indices) { vertices.push_back(allVertices[idx]); normals.push_back(allNormals[idx]); } return std::pair{std::move(vertices), std::move(normals)}; }
关于标准库的“解包”支持
标准库的std::views::zip专注于将多个范围打包为元组范围,反向的“解包”操作因为需要同时输出到多个容器,不属于单一范围的操作范畴,因此未被纳入标准。目前最实用的方案就是自定义工具函数或直接遍历,两者都能有效避免代码重复。
内容的提问来源于stack exchange,提问作者rubenvb
相关产品推荐
相关产品推荐

