std::ranges中是否有range-v3 views::cache1的替代方案?
std::ranges中views::cache1的替代实现
标准C++20/23的std::ranges库并没有内置对应range-v3中views::cache1的视图,但你可以轻松实现一个轻量版本——它的核心逻辑就是缓存最近迭代到的元素,避免对计算开销大的range重复求值,非常适合复杂计算、IO绑定的场景。
以下是符合标准ranges规范的cache1视图实现:
#include <ranges> #include <optional> template<std::ranges::input_range R> requires std::copyable<std::ranges::range_value_t<R>> class cache1_view : public std::ranges::view_interface<cache1_view<R>> { private: R base_; mutable std::optional<std::ranges::range_value_t<R>> cached_val_; mutable std::ranges::iterator_t<R> current_iter_ = std::ranges::begin(base_); mutable bool is_cached_ = false; public: cache1_view() = default; explicit cache1_view(R base) : base_(std::move(base)), current_iter_(std::ranges::begin(base_)) {} struct iterator { using value_type = std::ranges::range_value_t<R>; using difference_type = std::ranges::range_difference_t<R>; using iterator_category = std::input_iterator_tag; cache1_view* parent_ = nullptr; bool is_end_ = false; iterator() = default; explicit iterator(cache1_view* parent, bool is_end) : parent_(parent), is_end_(is_end) {} value_type operator*() const { if (!parent_->is_cached_) { parent_->cached_val_ = *parent_->current_iter_; parent_->is_cached_ = true; } return *parent_->cached_val_; } iterator& operator++() { if (!is_end_) { ++parent_->current_iter_; parent_->is_cached_ = false; is_end_ = (parent_->current_iter_ == std::ranges::end(parent_->base_)); } return *this; } iterator operator++(int) { auto tmp = *this; ++*this; return tmp; } bool operator==(const iterator& other) const { if (is_end_ && other.is_end_) return true; if (is_end_ != other.is_end_) return false; return parent_->current_iter_ == other.parent_->current_iter_; } }; iterator begin() { return iterator(this, false); } iterator end() { return iterator(this, true); } }; namespace views { inline constexpr auto cache1 = []<std::ranges::input_range R>(R&& r) { return cache1_view<std::views::all_t<R>>(std::views::all(std::forward<R>(r))); }; }
实现说明
- 该视图仅缓存当前迭代器指向的元素,每次迭代前进时会清除缓存标记,确保下一次取值时重新计算并缓存。
- 满足输入范围的要求,支持所有元素可拷贝的输入类型range。
- 完全兼容标准ranges的链式调用语法。
使用示例
假设你有一个计算开销极大的transform视图,用cache1包裹后,多次遍历同一个range时每个元素只会被计算一次:
#include <iostream> #include <chrono> #include <thread> // 模拟高开销计算 auto heavy_calculation = [](int x) { std::cout << "[Computing] " << x << "\n"; // 模拟耗时操作 std::this_thread::sleep_for(std::chrono::milliseconds(500)); return x * 3; }; int main() { auto cached_range = std::views::iota(1, 4) | std::views::transform(heavy_calculation) | views::cache1; // 第一次遍历:触发计算 std::cout << "First pass:\n"; for (int val : cached_range) { std::cout << val << " "; } std::cout << "\n"; // 第二次遍历:直接使用缓存,无计算输出 std::cout << "Second pass:\n"; for (int val : cached_range) { std::cout << val << " "; } std::cout << "\n"; return 0; }
运行后会看到,三次计算只会在第一次遍历时触发,第二次遍历直接读取缓存值。
内容的提问来源于stack exchange,提问作者Damir Tenishev
相关产品推荐
相关产品推荐

