我的自定义 std::ranges 迭代器缺少什么?

What am I missing in my custom std::ranges iterator?

我想为客户数据结构提供一个视图,它有自己的迭代器。我写了一个小程序来测试它,如下所示。如果我取消对 begin() 的注释,那么它就可以工作了。但是,如果我使用 DummyIter,则会出现编译错误。

在我的完整程序中,我实现了一个完整的迭代器,但为了简单起见,我在这里将其缩小到必要的功能。

#include <iostream>
#include <ranges>
#include <vector>

template<class T>
struct DummyIter
{
  using iterator_category = std::random_access_iterator_tag;
  using value_type = T;
  using difference_type = std::ptrdiff_t;

  DummyIter() = default;

  auto operator*() const { T t; return t; }

  auto& operator++() { return *this; }

  auto operator++(int val) { DummyIter tmp = *this; ++*this; return tmp; }

  auto operator==(const DummyIter& iter) const { return true; }
};

template<class V>
struct DummyView : std::ranges::view_interface<DummyView<V>>
{
  //auto begin() const { return std::ranges::begin(v); }
  auto begin() const { return DummyIter<int>(); }

  auto end() const { return std::ranges::end(v); }

  V v;
};

int main() {
  auto view = DummyView<std::vector<int>>();
  view | std::views::filter([](auto i) { return i > 0; });
}

我正在使用 GCC 11.1.0。我的迭代器中缺少什么才能使其符合范围?

error: no match for 'operator|' (operand types are 'DummyView<std::vector<int> >' and 'std::ranges::views::__adaptor::_Partial<std::ranges::views::_Filter, main()::<lambda(auto:15)> >')
   37 |   view | std::views::filter([](auto i) { return i > 0; });
      |   ~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |   |                        |
      |   |                        std::ranges::views::__adaptor::_Partial<std::ranges::views::_Filter, main()::<lambda(auto:15)> >
      |   DummyView<std::vector<int> >

这是行不通的,因为您的 beginend 的 return 类型不匹配。 所以基本上这些迭代器不能相互比较。

证明:

最低要求是 begin()end() 的结果具有可比性。 begin()end() 的不同类型在范围大小未知时很有用。这很好explanation of sentinel(在评论中提到)。

因为你的DummyView不符合range的概念,准确的说return类型的view.end()不能[=29] =]作为view.begin()的哨兵,因为DummyIter没有合适的operator==()来和std::vector<int>::const_iterator比较,也就是说没有方式知道迭代器是否已经到达范围的末尾。

解决方法是将 bool operator==(std::vector<int>::const_iterator) const 添加到您的 DummyIter

template<class T>
struct DummyIter {
  // ...
  bool operator==(std::vector<T>::const_iterator) const;
};

Demo

检查这种东西的范围的方法是:

  1. 确认您的迭代器是 input_iterator.
  2. 验证你的哨兵是sentinel_for你的迭代器。

这些检查会告诉您缺少什么功能。

在这种情况下,即:

using I = DummyIter<int>;
using S = std::vector<int>::const_iterator;

static_assert(std::input_iterator<I>);   // ok
static_assert(std::sentinel_for<S, I>);  // error

问题是 S 不是 sentinel_for<I>,因为它不具有可比性。您需要知道迭代何时停止,并且您没有该运算符 - 您的 DummyIter<T> 与另一个 DummyIter<T> 相当,但与您 return 从 end().

因此您要么需要在 DummyIter<T> 中添加另一个 operator==,要么 DummyView<V>::end() return 某种与 [= 相当的 DummySentinel<V> 15=]。这取决于实际问题,哪种方法更好,标准库中都有这两种方法的示例。