实现take_while_inclusive:包含不满足谓词元素的std::views::take_while变体
实现支持编译期的
views::take_while_inclusive视图(满足单次遍历需求) 我正在用std::ranges开发一个简单解析器,想通过try_parse_ints(str) | take_while(is_valid_int)的方式解析字符串中的整数,直到全部转换完成或出现转换失败。但出错时,我希望能获取导致错误的最后一个结果并返回给用户。
因此我需要实现一个views::take_while_inclusive,它会在返回第一个不满足谓词的元素之后才停止迭代。
我曾尝试用现有std::views实现,通过在谓词中隐藏static变量的技巧写出如下代码:
constexpr auto take_while_inclusive(auto&& predicate) { const auto custom_predicate = [predicate = std::forward<decltype(predicate)>(predicate)](auto&& value) { static bool found = true; return std::exchange(found, predicate(value)); }; return std::views::take_while(custom_predicate); }
但我知道take_while的谓词应当是const的,且这种实现依赖全局static状态,存在多实例冲突问题。同时我还有两个核心需求:
- 要像标准
std::views那样实时生成范围,且仅遍历输入范围一次 - 支持编译期使用
符合标准的自定义视图实现
下面是一个完全符合C++20范围标准的实现,满足所有需求:
#include <ranges> #include <iterator> #include <concepts> namespace views { template<std::ranges::input_range V, std::predicate<std::ranges::range_value_t<V>> Pred> requires std::ranges::view<V> class take_while_inclusive_view : public std::ranges::view_interface<take_while_inclusive_view<V, Pred>> { private: V base_; Pred pred_; template<bool Const> class iterator { private: using Base = std::conditional_t<Const, const V, V>; using BaseIter = std::ranges::iterator_t<Base>; BaseIter current_; BaseIter end_; Pred* pred_; bool done_ = false; public: using iterator_concept = std::input_iterator_tag; using value_type = std::ranges::range_value_t<Base>; using difference_type = std::ranges::range_difference_t<Base>; iterator() = default; constexpr iterator(BaseIter current, BaseIter end, Pred* pred) : current_(std::move(current)), end_(std::move(end)), pred_(pred), done_(false) {} constexpr value_type operator*() const { return *current_; } constexpr BaseIter operator->() const requires std::has_arrow<BaseIter> { return current_; } constexpr iterator& operator++() { if (!done_) { if (current_ != end_ && !(*pred_)(*current_)) { done_ = true; } else { ++current_; } } return *this; } constexpr iterator operator++(int) { auto tmp = *this; ++*this; return tmp; } friend constexpr bool operator==(const iterator& lhs, const iterator& rhs) { if (lhs.done_ || rhs.done_) { return lhs.done_ == rhs.done_; } return lhs.current_ == rhs.current_; } }; public: take_while_inclusive_view() = default; constexpr take_while_inclusive_view(V base, Pred pred) : base_(std::move(base)), pred_(std::move(pred)) {} constexpr auto begin() { return iterator<false>(std::ranges::begin(base_), std::ranges::end(base_), &pred_); } constexpr auto begin() const requires std::ranges::range<const V> && std::predicate<const Pred, std::ranges::range_value_t<const V>> { return iterator<true>(std::ranges::begin(base_), std::ranges::end(base_), const_cast<Pred*>(&pred_)); } constexpr auto end() { return iterator<false>(std::ranges::end(base_), std::ranges::end(base_), &pred_); } constexpr auto end() const requires std::ranges::range<const V> { return iterator<true>(std::ranges::end(base_), std::ranges::end(base_), const_cast<Pred*>(&pred_)); } }; namespace detail { struct take_while_inclusive_fn { template<std::ranges::input_range R, std::predicate<std::ranges::range_value_t<R>> Pred> requires std::ranges::viewable_range<R> constexpr auto operator()(R&& r, Pred pred) const { return take_while_inclusive_view<std::views::all_t<R>, Pred>(std::views::all(std::forward<R>(r)), std::move(pred)); } template<std::predicate Pred> constexpr auto operator()(Pred pred) const { return [pred = std::move(pred)](std::ranges::input_range auto&& r) requires std::ranges::viewable_range<decltype(r)> { return take_while_inclusive_view<std::views::all_t<decltype(r)>, Pred>(std::views::all(std::forward<decltype(r)>(r)), std::move(pred)); }; } }; } // namespace detail inline constexpr detail::take_while_inclusive_fn take_while_inclusive; } // namespace views
实现特点
- 编译期支持:所有核心成员函数都标记为
constexpr,只要输入范围和谓词支持编译期操作,就能在编译期完成遍历和筛选。 - 单次遍历:迭代器仅遍历输入范围一次,每次
++操作仅移动一次底层迭代器,符合输入迭代器的性能要求。 - 无全局状态:迭代状态(是否已遇到不满足谓词的元素)存储在迭代器实例内部,不会出现多视图实例冲突问题。
- 标准兼容性:继承自
std::ranges::view_interface,完全满足std::ranges::view概念,可以和其他标准视图自由组合。
使用示例
constexpr bool is_valid_int(int x) { return x >= 0; } constexpr std::array<int, 5> nums = {1, 2, -3, 4, 5}; // 结果包含1, 2, -3 constexpr auto result = nums | views::take_while_inclusive(is_valid_int);
内容的提问来源于stack exchange,提问作者fourmisnuee
相关产品推荐
相关产品推荐

