Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What should I do to make my container work with ranges?

I have a simple container:

template <class T, class Allocator = std::allocator<T>>
class ring
{
public:

    using value_type = T;
    using allocator_type = Allocator;
    using size_type = std::size_t;
    using difference_type = std::ptrdiff_t;
    using reference = T &;
    using const_reference = const T &;
    using pointer = T *;
    using const_pointer = const T *;

private:

    template <class E>
    class ring_iterator
    {
    public:

        using iterator_category = std::random_access_iterator_tag;
        using value_type = E;
        using difference_type = std::ptrdiff_t;
        using reference = E &;
        using pointer = E *;

        ring_iterator(const ring_iterator& other) = default;
        ring_iterator(ring_iterator&& other) = default;

        ring_iterator& operator = (const ring_iterator& other);

        pointer operator-> () const;

        reference operator* () const;

        ring_iterator& operator++ ();

        ring_iterator operator++ (int);

        ring_iterator& operator-- ();

        ring_iterator operator-- (int);

        ring_iterator& operator += (difference_type diff);

        ring_iterator& operator -= (difference_type diff);

        ring_iterator operator + (difference_type diff) const;

        ring_iterator operator - (difference_type diff) const;

        difference_type operator - (const ring_iterator& other) const;

        bool operator == (const ring_iterator& other) const;

        bool operator != (const ring_iterator& other)  const;

        bool operator < (const ring_iterator& other) const;

        operator ring_iterator<const E>() const;
    };

public:

    using iterator = ring_iterator<T>;
    using const_iterator = ring_iterator<const T>;
    using reverse_iterator = std::reverse_iterator<iterator>;
    using const_reverse_iterator = std::reverse_iterator<const_iterator>;

    ring(Allocator alloc = {});

    ring(size_type cap, Allocator alloc = {});

    ring(const ring& other);

    ring(ring&& other);

    ring& operator = (const ring& other);

    ring& operator = (ring&& other);

    ~ring();

    reference front();
    reference back();

    const_reference front() const;
    const_reference back() const;

    void push_front(const value_type& val);

    void push_front(value_type&& val);

    void push_back(const value_type& val);

    void push_back(value_type&& val);

    void pop_front();

    void pop_back();

    void reserve(size_t);

    void clear();

    size_type size() const;
    
    size_type capacity() const;
    
    bool empty() const;
    
    bool full() const;

    reference operator[](size_type index);

    const_reference operator[](size_type index) const;

    reference at(size_type index);

    const_reference at(size_type index) const;

    iterator begin();
    const_iterator begin() const;
    const_iterator cbegin() const;

    iterator end();
    const_iterator end() const;
    const_iterator cend() const;

    reverse_iterator rbegin();
    const_reverse_iterator rbegin() const;
    const_reverse_iterator crbegin() const;

    reverse_iterator rend();
    const_reverse_iterator rend() const;
    const_reverse_iterator crend() const;
};

What should I do to make the code below compile?

#include <vector>
#include <ranges>

//std::vector<int> v; //compiles
ring<int> v;     //does not compile

auto range = v | std::views::transform([](int n) { return n * n; });

MSVC compiler error:

error C2678: binary '|': no operator found which takes a left-hand operand of type 'ring<int,std::allocator<int>>' (or there is no acceptable conversion)
like image 962
Dmitriano Avatar asked Sep 06 '21 06:09

Dmitriano


People also ask

What can be stored in a container?

In general, you can store all kinds of executable files in containers, for example, configuration files, software code, libraries, and binary programs. By computing environments, we mean the local systems, on-premises data centers, and cloud platforms managed by various service providers. ‍ What Is a Container in Cloud?

What are the best tips for a successful container garden?

Here are some of our best tips for successful container gardens . While this may sound like an odd first tip, it can be a matter of life and death for your plants. When there isn't a big enough hole or holes for water to get out of your pot, your soil becomes too wet and the roots of your plants can rot which causes the plant to die.

How to decorate your home with containers?

Make your containers out of found items or haunt yard sales and second-hand stores. If you have a formal entrance and want a more tailored container look choose large containers in classic shapes and plant them with luxurious and eye-catching plants. Whatever your style, there are plants that can work for you.

How do I use the w3-container class?

The w3-container class adds a 16px left and right padding to any HTML element. The w3-container class is the perfect class to use for all HTML container elements like: <div>, <article>, <section>, <header>, <footer>, <form>, and more. To use a container, just add a w3-container class to any element: To add a color, just add a w3-color class:


2 Answers

So... after a lot of investigation:

Your iterator must have a public default constructor.


What should I do to make my container work with ranges?

It should satisfy the concept std::ranges::range:

static_assert(std::ranges::range<ring<int>>);

but it doesn't and the error messages are not helpful. So we look at the concept itself:

template< class T >
concept range = requires(T& t) {
  ranges::begin(t); // equality-preserving for forward iterators
  ranges::end  (t);
};

ranges::begin(v) is well defined, but ranges::end(v) gives error "error: no match for call to '(const std::ranges::__cust_access::_End) (ring&)'" with, again, no helpful error messages.

So now we look over std::ranges::end:

template< class T >
    requires /* see below */
constexpr std::sentinel_for<ranges::iterator_t<T>> auto end(T&& t);

The documentation here is a bit iffy, but the the failed requirement here is:

static_assert(
    std::sentinel_for<ring<int>::iterator, ring<int>::iterator>
);

i.e. the end iterator should be a sentinel for the begin iterator.

It is here where we reach at our first useful error message:

error: static assertion failed

   89 |     std::sentinel_for<ring<int>::iterator, ring<int>::iterator>
      |     ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

note: constraints not satisfied

[...]

opt/compiler-explorer/gcc-trunk-20210906/include/c++/12.0.0/concepts:137:30: note: the expression is_constructible_v<_Tp, _Args ...> [with _Tp = ring<int, std::allocator<int> >::ring_iterator<int>; _Args = {}] evaluated to 'false'

  137 |       = destructible<_Tp> && is_constructible_v<_Tp, _Args...>;
      |                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

So there you have it ring<int>::ring_iterator<int> must have a publicly available default constructor.

like image 183
bolov Avatar answered Oct 06 '22 07:10

bolov


What should I do to make my container work with ranges?

You should design the container to conform to the "Range" concept.

In short, the container should to provide member functions begin and end which should return iterator and a sentinel. The end sentinel must be reachable from begin iterator. The sentinel type may be the same as the iterator. The iterator type must conform to the "Iterator" concept.

What should I do to make the code below compile?

ring_iterator in your attempt isn't default initialisable, and as such it isn't an Iterator and thus the container isn't a range. Add a default constructor to solve the problem.

like image 1
eerorika Avatar answered Oct 06 '22 08:10

eerorika