I'd like to provide a view for a customer data structure, with it's own iterator. I wrote a small program to test it out, shown below. It I uncomment begin(), then it works. But if I use DummyIter, then I get a compile error.
In my full program, I've implemented a full iterator but for simplicity, I narrowed it down to the necessary functions here.
#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; });
}
I'm using GCC 11.1.0. What am I missing in my iterator for it to be ranges compliant?
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> >
The way to check this kind of thing for ranges is:
input_iterator.sentinel_for your iterator.Those are the checks that will tell you what functionality you're missing.
In this case, that's:
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
And the issue there is that S isn't a sentinel_for<I> because it's not equality comparable. You need to know when the iteration stops, and you don't have that operator - your DummyIter<T> is comparable to another DummyIter<T>, but not to what you're returning from end().
So you either need to add another operator== to DummyIter<T>, or have DummyView<V>::end() return some kind of DummySentinel<V> that is comparable to DummyIter<T>. It depends on the real problem which is the better approach, there are examples of both in the standard library.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With