无需创建拼接向量,如何遍历多个同类型std::vector的拼接序列?
遍历多个容器的串行拼接序列(无需创建新容器)
C++标准库本身没有直接提供这种工具,但可以自行实现轻量级适配器,或借助第三方库的现成组件来实现需求。
1. 自行实现zup工具
基于C++20范围特性,可以快速编写一个支持多容器串行遍历的适配器,完全复用原容器的迭代器,不会创建新容器:
#include <vector> #include <iostream> #include <ranges> #include <tuple> namespace fancy_util { template<std::ranges::input_range... Ranges> requires (std::same_as<std::ranges::range_value_t<Ranges>, std::ranges::range_value_t<decltype((Ranges{}, ...))>> && ...) struct concatenated_range { std::tuple<Ranges...> ranges; struct iterator { using value_type = std::ranges::range_value_t<std::tuple_element_t<0, decltype(ranges)>>; using reference = const value_type&; using pointer = const value_type*; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; size_t current_range_idx; std::tuple<std::ranges::iterator_t<Ranges>...> its; std::tuple<std::ranges::sentinel_t<Ranges>...> ends; iterator(size_t idx, decltype(its) its_, decltype(ends) ends_) : current_range_idx(idx), its(std::move(its_)), ends(std::move(ends_)) {} reference operator*() const { return *std::get<current_range_idx>(its); } pointer operator->() const { return std::addressof(**this); } iterator& operator++() { ++std::get<current_range_idx>(its); if (std::get<current_range_idx>(its) == std::get<current_range_idx>(ends)) { if constexpr (sizeof...(Ranges) > current_range_idx + 1) { current_range_idx++; } } return *this; } iterator operator++(int) { auto tmp = *this; ++*this; return tmp; } friend bool operator==(const iterator& lhs, const iterator& rhs) { if (lhs.current_range_idx != rhs.current_range_idx) { return false; } return std::get<lhs.current_range_idx>(lhs.its) == std::get<lhs.current_range_idx>(rhs.its); } friend bool operator!=(const iterator& lhs, const iterator& rhs) { return !(lhs == rhs); } }; iterator begin() { return iterator{0, std::apply([](auto&&... r) { return std::make_tuple(std::ranges::begin(r)...); }, ranges), std::apply([](auto&&... r) { return std::make_tuple(std::ranges::end(r)...); }, ranges) }; } iterator end() { return iterator{sizeof...(Ranges)-1, std::apply([](auto&&... r) { return std::make_tuple(std::ranges::end(r)...); }, ranges), std::apply([](auto&&... r) { return std::make_tuple(std::ranges::end(r)...); }, ranges) }; } }; template<std::ranges::input_range... Ranges> auto zup(Ranges&&... ranges) { return concatenated_range<std::decay_t<Ranges>...>{std::make_tuple(std::forward<Ranges>(ranges)...)}; } } // namespace fancy_util int main() { std::vector<int> vec1{0,1,2}; std::vector<int> vec2{3,4,5}; for (const auto& e : fancy_util::zup(vec1, vec2)){ std::cout << e << ", "; } // 输出: 0, 1, 2, 3, 4, 5, return 0; }
这个实现支持任意多个同类型输入范围,遍历过程无额外内存开销。
2. 使用第三方库
如果不想自行实现,Boost.Range中的boost::range::join可以直接实现需求,它将多个范围拼接为一个可遍历的视图,无需复制元素:
#include <vector> #include <iostream> #include <boost/range/join.hpp> int main() { std::vector<int> vec1{0,1,2}; std::vector<int> vec2{3,4,5}; for (const auto& e : boost::join(vec1, vec2)){ std::cout << e << ", "; } return 0; }
Boost.Range的组件经过充分测试,兼容性强,支持多种容器类型。
3. C++23的替代方案(若可用)
C++23新增的std::views::join_with主要用于处理嵌套范围,但可以通过包装单个容器实现类似效果:
#include <vector> #include <iostream> #include <ranges> int main() { std::vector<int> vec1{0,1,2}; std::vector<int> vec2{3,4,5}; auto concatenated = std::views::single(vec1) | std::views::join_with(vec2); for (const auto& e : concatenated){ std::cout << e << ", "; } return 0; }
这种写法不如专门的拼接视图直观,优先级低于前两种方案。
内容的提问来源于stack exchange,提问作者vlad_tepesch
相关产品推荐
相关产品推荐

