基于输入迭代器创建viewable range遇编译报错,求排查
关于
std::ranges::subrange无法作为viewable range的问题分析 问题背景
实现了一个类zip的lattice_iterator,通过递增当前所有输入中的最小字段进行迭代。主函数中使用std::ranges::subrange包装该迭代器与std::unreachable_sentinel,但编译器报错该subrange不是viewable range。
查阅资料得知,非viewable range通常是不可复制的左值视图,而输入迭代器(如generator迭代器)仅支持移动不支持复制,但generator本身是viewable range且兼容std::views::filter,因此怀疑自身迭代器设计存在错误。
实现代码
template <typename T> struct lattice_iterator { public: using difference_type = std::ptrdiff_t; using value_type = std::vector<T>; using iterator_category = std::input_iterator_tag; private: struct wrapped_iter { T value_; std::move_only_function<T(void)> adder_; wrapped_iter(const wrapped_iter&) = delete; wrapped_iter(wrapped_iter&&) = default; wrapped_iter(std::ranges::input_range auto&& range) { auto iter = range.begin(); value_ = *iter; adder_ = [iter_ = std::move(iter)]() mutable { return *++iter_; }; } bool operator<(wrapped_iter const& rhs) const { return value_ < rhs.value_; } wrapped_iter& operator++() { value_ = adder_(); return *this; } T& operator*() { return value_; } }; public: std::vector<wrapped_iter> iters_{}; value_type values_{}; template <std::ranges::input_range... R> requires(std::convertible_to<std::ranges::range_value_t<R>, T> && ...) lattice_iterator(R&&... ranges) { (iters_.emplace_back(std::forward<R>(ranges)), ...); values_ = iters_ | std::views::transform([](auto&& iter) { return iter.value_; }) | std::ranges::to<std::vector>(); } lattice_iterator(lattice_iterator const&) = delete; lattice_iterator(lattice_iterator&&) = default; lattice_iterator() = delete; lattice_iterator& operator=(lattice_iterator&& rhs) = default; value_type operator*() const { return values_; } lattice_iterator& operator++() { std::size_t min_idx = std::ranges::min( std::views::iota(0) | std::views::take(iters_.size()), {}, [this](std::size_t idx) { return values_[idx]; }); values_[min_idx] = *++iters_[min_idx]; return *this; } void operator++(int) { ++*this; } }; std::generator<int> get_triangles() { for (int n = 1;; ++n) { co_yield n*(n + 1) / 2; } } std::generator<int> get_pentagonal() { for (int n = 1;; ++n) { co_yield n * (3 * n - 1) / 2; } } std::generator<int> get_hexagonal() { for (int n = 1;; ++n) { co_yield n * (2 * n - 1); } } int main() { auto lattice = std::ranges::subrange( lattice_iterator<int>{get_triangles(), get_pentagonal(), get_hexagonal()}, std::unreachable_sentinel); static_assert(std::same_as<std::vector<int>, std::ranges::range_value_t<decltype(lattice)>>); auto nums = std::views::filter( lattice, [](auto&& pack) -> bool { static_assert(std::same_as<std::vector<int>, decltype(pack)>); auto p0 = pack.front(); return std::ranges::all_of(pack, [&p0](auto px) { return px == p0; }); }) | std::views::transform([](auto&& pack) { return pack.front(); }) | std::views::take(3) | std::ranges::to<std::vector>(); std::cout << nums[1] << ' ' << nums[2] << '\n'; }
疑问点
- 编译器报错创建的subrange不是
viewable range - 输入迭代器(如
generator迭代器)仅支持移动不支持复制,但generator本身是viewable range且兼容std::views::filter,为何自己的迭代器包装后不行?
问题分析与解决
核心原因:lattice_iterator不满足std::input_iterator的完整要求
虽然给lattice_iterator标记了std::input_iterator_tag,但它并未满足C++标准中输入迭代器的全部语义要求,尤其是以下两点:
- 迭代器必须可默认构造:标准要求输入迭代器类型必须提供默认构造函数,但你的
lattice_iterator显式删除了默认构造函数(lattice_iterator() = delete;)。 - 迭代器必须支持与哨兵的相等比较:
std::subrange需要迭代器能和传入的哨兵(std::unreachable_sentinel)做相等判断,但你的lattice_iterator完全没有实现对应的operator==。
修复方案
- 添加默认构造函数:给
lattice_iterator添加默认构造函数,满足输入迭代器的要求:lattice_iterator() = default; - 实现与
std::unreachable_sentinel的相等比较:由于迭代器是无限循环的,直接返回false即可:bool operator==(std::unreachable_sentinel_t) const { return false; }
修复后的关键代码片段
template <typename T> struct lattice_iterator { public: // ... 原有成员 ... // 添加默认构造函数 lattice_iterator() = default; // ... 原有构造函数 ... // 实现与unreachable_sentinel的相等比较 bool operator==(std::unreachable_sentinel_t) const { return false; } // ... 原有成员函数 ... };
为什么std::generator可以正常工作?
std::generator的迭代器满足输入迭代器的全部要求:支持默认构造、相等比较(与自身或哨兵),并且其生成的range满足viewable_range的条件——它是一个符合标准的右值视图,而你的subrange包装的是不满足输入迭代器要求的不可复制左值迭代器,因此无法成为viewable range。
内容的提问来源于stack exchange,提问作者haleyk
相关产品推荐
相关产品推荐

