Search code examples
c++c++20std-rangesrange-v3

Why does the random access property of iota_view depend on the element type? And how to write a Foo such that iota(Foo{1}, Foo{10}) is random-access?


The following code fails to compile if you uncomment the commented line. (Compiler Explorer)

Why is that?

I imagine the reason is that the iota_view of Foos doesn't know how to advance a Foo. But how do I make it know?

#include <iostream>
#include <ranges>

struct Foo {
    int x;
    Foo() : x{-1} {}
    Foo(int x) : x{x} {}
    using difference_type = int;
    friend auto operator<=>(Foo const& a, Foo const& b) {
        return a.x <=> b.x;
    }
    friend bool operator==(Foo const& a, Foo const& b) {
        return a.x == b.x;
    }
    auto& operator++() {
        ++x;
        return *this;
    }
    auto operator++(int) {
        auto r = *this;
        ++x;
        return r;
    }
};

int main () {
    auto ints = std::views::iota(1, 30);
    for (auto i : ints) {}
    std::cout << ints[15] << std::endl;
    auto foos = std::views::iota(Foo{1}, Foo{30});
    for (auto i : foos) {}
    //std::cout << foos[15] << std::endl;
}

The example usees , but I'm interested in an answer for too, should they differ.


Solution

  • Take a look at this page: https://en.cppreference.com/w/cpp/ranges/iota_view/iterator

    To be a random access range, the type needs to be "advanceable". This requires operator+, operator+=, operator- and operator-= (and operator-- to be a bidirectional range).

    Advancing an iterator by n will just add n to the current value with operator+= or operator+.

    Example implementations:

    struct Foo {
        // ...
        Foo& operator--() { --x; return *this; }
        Foo operator--(int) { auto r = *this; --*this; return r; }
        friend Foo operator+(Foo l, int r) { return Foo{ l.x + r }; }
        friend Foo operator+(int l, Foo r) { return Foo{ l + r.x }; }
        friend Foo operator-(Foo l, int r) { return Foo{ l.x - r }; }
        friend int operator-(Foo l, Foo r) { return l.x - r.x; }
        Foo& operator+=(int r) { return *this = *this + r; }
        Foo& operator-=(int r) { return *this = *this - r; }
    };