如何为可调用迭代器适配范围for循环且无需预读下一元素
无需预读的可调用迭代器范围适配方案
针对你提出的需求——将返回std::optional<T>的可调用对象适配为范围for循环可用的迭代器,同时避免预读行为,这里提供两种可行方案:
方案一:C++17兼容的手动迭代器实现
核心思路是延迟获取第一个元素,仅在第一次解引用迭代器时才调用可调用对象;递增操作负责获取下一个元素,并在耗尽时标记为结束状态,彻底避免预读。
#include <functional> #include <iostream> #include <optional> #include <stdexcept> #include <vector> std::function<std::optional<int>()> callableIterator() { return [value = 0]() mutable -> std::optional<int> { if (value == 10) { return {}; } return value++; }; } template <typename It> struct CallableIterable { It it; using T = std::decay_t<decltype(*it())>; CallableIterable(It&& it) : it(std::move(it)) {} struct Iterator { It* it_ptr; std::optional<T> current; // 构造begin迭代器:仅持有可调用对象指针,不提前调用 explicit Iterator(It* ptr) : it_ptr(ptr), current(std::nullopt) {} // 构造end迭代器:用空指针标记结束状态 Iterator() : it_ptr(nullptr), current(std::nullopt) {} Iterator& operator++() { if (it_ptr) { // 递增时才获取下一个值 current = (*it_ptr)(); // 若返回nullopt,标记为结束状态 if (!current) { it_ptr = nullptr; } } return *this; } // 后置递增(range for不会用到,仅为迭代器完整性实现) Iterator operator++(int) { auto temp = *this; ++*this; return temp; } bool operator!=(const Iterator& other) const { // 通过指针是否相等判断是否到达终点 return it_ptr != other.it_ptr; } T operator*() { if (!it_ptr) { throw std::runtime_error("解引用已耗尽的迭代器"); } // 第一次解引用时才获取第一个元素 if (!current) { current = (*it_ptr)(); if (!current) { it_ptr = nullptr; throw std::runtime_error("解引用已耗尽的迭代器"); } } return *current; } }; Iterator begin() { return Iterator(&it); } Iterator end() { return Iterator(); } }; int main() { for (auto value : CallableIterable(callableIterator())) { std::cout << value << '\n'; } return 0; }
逻辑说明
- begin迭代器初始化时仅持有可调用对象的指针,不会提前调用它;
- 进入循环时先判断
begin != end(此时begin指针非空,end指针为空,判断成立); - 第一次解引用
*begin时,才首次调用可调用对象获取第一个值; - 循环体执行完毕后,调用
++begin获取下一个值,若返回nullopt则将指针置空,标记为结束状态; - 下一次循环判断时,指针为空,循环终止。
方案二:C++20范围库简洁实现
如果可以使用C++20的<ranges>库,直接用标准适配器组合即可,无需手动编写迭代器,完全按需执行无预读:
#include <functional> #include <iostream> #include <optional> #include <ranges> #include <vector> std::function<std::optional<int>()> callableIterator() { return [value = 0]() mutable -> std::optional<int> { if (value == 10) { return {}; } return value++; }; } template <std::invocable F> auto make_optional_range(F&& f) { using OptType = std::invoke_result_t<F>; static_assert(std::is_same_v<OptType, std::optional<std::decay_t<typename OptType::value_type>>>, "传入的可调用对象必须返回std::optional<T>"); return std::views::generate(std::forward<F>(f)) | std::views::take_while([](const auto& opt) { return opt.has_value(); }) | std::views::transform([](auto opt) { return std::move(*opt); }); } int main() { for (auto value : make_optional_range(callableIterator())) { std::cout << value << '\n'; } return 0; }
逻辑说明
std::views::generate:每次迭代才调用可调用对象生成std::optional<T>;std::views::take_while:当生成的元素为nullopt时立即停止迭代;std::views::transform:将std::optional<T>解包为T,供循环直接使用。
内容的提问来源于stack exchange,提问作者user3612643
相关产品推荐
相关产品推荐

