8

I am having trouble using view_facade (from range-v3) to create a view that provides both const and non-const access. As an example, I tried modifying the view_facade test (in test/view_facade.cpp) to allow non-const access (by default it only allows const access):

struct MyRange
  : ranges::range_facade<MyRange>
{
private:
    friend struct ranges::range_access;
    std::vector<int> ints_;

    template <bool isConst>
    struct cursor
    {
    private:
        using It = typename std::conditional<isConst, std::vector<int>::const_iterator, std::vector<int>::iterator>::type;
        using RefType = typename std::conditional<isConst, int const&, int&>::type;
        It iter;
    public:
        cursor() = default;
        cursor(It it)
          : iter(it)
        {}
        RefType current() const
        {
            return *iter;
        }
    //...
    };
/*    // Uncommenting these overloads will cause an error, below.
    cursor<true> begin_cursor() const
    {
        return {ints_.begin()};
    }
    cursor<true> end_cursor() const
    {
        return {ints_.end()};
    }
*/
    cursor<false> begin_cursor()
    {
        return {ints_.begin()};
    }
    cursor<false> end_cursor()
    {
        return {ints_.end()};
    }
public:
    MyRange()
      : ints_{1, 2, 3, 4, 5, 6, 7}
    {}
};

int main() {
    MyRange nc;
    int& nci = *nc.begin();  // error here when const overloads present.
}

Full code here.

This works fine with the const overloads of begin_cursor and end_cursor commented out. However, if I add those overloads back in, the following error is generated on the indicated line (GCC 5.1):

error: binding 'const int' to reference of type 'int&' discards qualifiers

It seems to be selecting the const version, giving me a const iterator. What I want is: const iterators for const objects, and non-const iterators for non-const objects. How can I achieve that?

edflanders
  • 213
  • 1
  • 6

1 Answers1

9

view_facade is for building views. Views refer to data they don't own. They are like pointers in that logically they are indirections. And like pointers, top-level const should have no effect on the const-ness of the data referenced. That is whether you dereference an int* or an int*const, the result is the same: int&.

Your view is not a view. It owns its data. (See the vector<int> ints_ data member.) Trying to use view_facade to turn this data structure into a view is bound to lead to frustration. This is very much by design. Views are distinct from containers. The Range-v3 library doesn't come with a container facade, sorry.

(What's going on: since views represent indirection, view_facade tries very hard to make const and non-const begin() and end() return the same types. If cursor_begin() const is present, that one is always chosen. Always. When this breaks code, it's generally because that code is confusing containers with views.)

Eric Niebler
  • 5,927
  • 2
  • 29
  • 43
  • I guess I didn't understand what was meant by a view not owning its data. The example I posted was pretty much straight out of the tests, but what I was actually trying to do was wrap a pointer + size in a range-like interface. Since the view wouldn't own the pointer in the sense of allocating/deallocating the data (it's managed by a third-party library), I thought a view_facade was appropriate. But, your explanation makes sense. Thanks for your answer. – edflanders Jul 17 '15 at 01:19
  • 2
    A pointer/length pair *is* a view. `view::counted` is precisely that. If you replace your `vector` member with pointer/length, things should work much better. – Eric Niebler Jul 17 '15 at 20:40
  • Ah! `view::counted` is a perfect example. Not sure how I missed it. – edflanders Jul 17 '15 at 21:20
  • 1
    "When this breaks code, it's generally because that code is..." already broken. – Casey Jul 21 '15 at 07:10
  • Although container adaptors *are* interesting and useful (see `std::stack` for instance), I'm not sure this is the right time to further expand the scope of range-v3. I hope to spend more of my time polishing and proposing what's already there, and less time adding new features that I'll need to maintain. – Eric Niebler Jul 21 '15 at 17:46
  • Agreed! Just thought it'd be impolite not to offer. Thanks for your time. – edflanders Jul 22 '15 at 13:11