You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于输入迭代器创建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++标准中输入迭代器的全部语义要求,尤其是以下两点:

  1. 迭代器必须可默认构造:标准要求输入迭代器类型必须提供默认构造函数,但你的lattice_iterator显式删除了默认构造函数(lattice_iterator() = delete;)。
  2. 迭代器必须支持与哨兵的相等比较:std::subrange需要迭代器能和传入的哨兵(std::unreachable_sentinel)做相等判断,但你的lattice_iterator完全没有实现对应的operator==。

修复方案

  1. 添加默认构造函数:给lattice_iterator添加默认构造函数,满足输入迭代器的要求:
    lattice_iterator() = default;
    
  2. 实现与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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 11:32:06