dvd
dvd

Reputation: 1034

range-v3 and view_facade, cannot model ForwardRange

This reduced test case (written following the example in the user manual) does not compile

#include <range/v3/all.hpp>
#include <vector>

using v = std::vector<int>;

class rows : public ranges::view_facade<rows> {
  public:
    rows() = default;
    explicit rows(const v& data) : it_(data.begin()), end_(data.end()) {}

  private:
    friend ranges::range_access;
    v::const_iterator it_;
    v::const_iterator end_;

    const int& read() const {
        return *it_;
    }

    bool equal(ranges::default_sentinel) const {
        return it_ == end_;
    }

    void next() {
        ++it_;
    }
};

int main() {
    v data{10, 20, 30, 40};
    auto rng = rows(data) | ranges::view::unique;
}

The compilation fails with a static_assert since, according to view::unique, my range does not model the ForwardRange concept

But if I rewrite my class to use an explicit cursor, the compilation is successful

class rows : public ranges::view_facade<rows> {
  public:
    rows() = default;
    explicit rows(const v& data) : data_{&data} {}

  private:
    friend ranges::range_access;

    const v* data_;

    struct cursor {
        cursor() = default;
        cursor(v::const_iterator iter) : it{iter} {}

        const int& read() const {
            return *it;
        }
        bool equal(const cursor& other) const {
            return it == other.it;
        }
        void next() {
            ++it;
        }

        v::const_iterator it;
    };

    cursor begin_cursor() const {
        return {data_->begin()};
    }
    cursor end_cursor() const {
        return {data_->end()};
    }
};

Why the first class is not a ForwardRange and the second instead is ok? view_facade<>::(begin|end)_cursor() by default returns an instance of the derived class, so I don't understand why it does not work.

I've added a static assert to be sure that ranges::range_access::single_pass_t is false, so I suspect that the problem is related with the ForwardIterator concept.

Upvotes: 4

Views: 462

Answers (1)

Casey
Casey

Reputation: 42554

You define equal(ranges::default_sentinel) const but not equal(const rows&), so the iterator type of your range will satisfy EqualityComparableWith<ranges::default_sentinel> but not EqualityComparable. Forward iterators (and stronger) are required to satisfy EqualityComparable, so rows satisfies InputRange but not ForwardRange.

I suggest you define:

bool equal(const rows& that) const {
    return it_ == that.it_;
}

and the program behaves as you expect.

Upvotes: 4

Related Questions