C++ Custom Iterator - c++

I have two classes that implement a data structure similar to a linked list. We'll call them Node and List. To trim the fat of all the constructors and other methods, they essentially look like this:
class Node {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = Node;
using pointer = Node*;
using reference = Node&;
using size_type = size_t;
using difference_type = std::ptrdiff_t;
// Accessor functions
bool operator!= (value_type const& n) const;
bool operator== (value_type const& n) const;
reference operator*();
pointer operator&();
pointer operator->();
value_type const& operator++() const;
};
class List {
private:
typedef Node* iterator;
typedef const Node* const_iterator;
public:
// Iterators
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
// Operator overloads
Node& operator*() const;
const List* operator&() const;
const List* operator->() const;
};
I am trying to achieve something to the effect of:
for (Node n : List)
{
// Some stuff
}
Yet I keep getting the (Visual Studio) error this range-based 'for' statement requires a suitable "begin" function and none was found
What am I missing?

Related

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)
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.
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.

How does vector's begin() and end() work, and how should I write it for my custom vector?

I am currently studying Bjarne's PPP, and I don't really understand how begin() and end() works, and how I am supposed to implement it.
Here is my code for the vector.
template<typename T> class vector {
int sz;
T *elem;
int space;
std::allocator<T> alloc;
public:
vector():sz{0},elem{nullptr},space{0}{}
explicit vector(int s):sz{s},elem{new T[s]}, space{s}{
for(int i = 0; i<sz; i++) elem[i]=0;
}
T &at(int n);
const T &at(int n) const;
void reserve(int newalloc);
void resize(int newsize, T def=T());
int capacity() const { return space;}
void push_back(int d);
vector &operator=(const vector &a);
T *begin();
const T *begin() const;
T *end();
const T *end() const;
unsigned long size();
};
begin() and end() should return an iterator. Basically, iterator is a class that implements variables such as iterator_category and value_type, which let other functions to know how to use it.
It should also implement functions required by the iterator_category that you choose to use.
For your a vector, you need a random access iterator. You need two classes: constant access iterator (returned by const function) and non-constant access iterator. Usually you would derive the non-constant class from the constant class.
The positions pointed by the iterators: begin points to the beginning of the data, and end is begin() + size().
template<typename T>
class vector_iterator {
public:
typedef std::random_access_iterator_tag iterator_category;
typedef T value_type;
typedef std::ptrdiff_t difference_type;
typedef const value_type& reference;
typedef const value_type* pointer;
// Functions come here
// You must implement some operators
reference operator*() const { /* must implement */ }
vector_iterator operator+(const difference_type) const { /* must implement */ }
// Some operators can use the operations that are already implemented
vector_iterator operator-(const difference_type n) const { return *this + (-n); }
reference operator[](const difference_type n) const { return *(*this + n); }
};

Accessing an outer public member from an inner class

I have the following classes and I am trying to overload the operator* from the inner class iterator
#ifndef __LISTMAP_H__
#define __LISTMAP_H__
#include "xless.h"
#include "xpair.h"
template <typename Key, typename Value, class Less=xless<Key>>
class listmap {
public:
using key_type = Key;
using mapped_type = Value;
using value_type = xpair<const key_type, mapped_type>;
private:
Less less;
struct node;
struct link {
node* next{};
node* prev{};
link (node* next, node* prev): next(next), prev(prev){}
};
struct node: link {
value_type value{};
node (node* next, node* prev, const value_type&);
};
node* anchor() { return static_cast<node*> (&anchor_); }
link anchor_ {anchor(), anchor()};
public:
class iterator;
listmap(){};
listmap (const listmap&) = default;
listmap& operator= (const listmap&) = default;
~listmap();
iterator insert (const value_type&);
iterator find (const key_type&);
iterator erase (iterator position);
iterator begin() { return anchor()->next; }
iterator end() { return anchor(); }
bool empty() const { return begin() == end(); }
};
template <typename Key, typename Value, class Less>
class listmap<Key,Value,Less>::iterator {
private:
friend class listmap<Key,Value>;
listmap<Key,Value,Less>::node* where {nullptr};
iterator (node* where): where(where){};
public:
iterator(){}
value_type& operator*();
value_type* operator->();
iterator& operator++(); //++itor
iterator& operator--(); //--itor
void erase();
bool operator== (const iterator&) const;
bool operator!= (const iterator&) const;
};
template <typename Key, typename Value, class Less>
value_type& listmap<Key,Value,Less>::iterator<Key,Value,Less>::operator*()
{
return where->value;
}
#include "listmap.tcc"
#endif
The problem is that value_type is a public member from the class listmap and it's not static, so I don't know how to complete the declaration of operator*(). I wouldn't like to fix the bug by changing the structure of the code. Ex: making
using value_type = xpair<const key_type, mapped_type>;
Global. I am just wondering if there is some other trick I can use to access value_type.
....edit: I have no idea how the inner class recognizes value_type
It's barely the same as for iterator, you just have to add typename keyword
typename listmap<Key,Value,Less>::value_type
staticness doesn't matter for a type.
The alias1 inside iterator
template <typename Key, typename Value, class Less>
class listmap<Key,Value,Less>::iterator {
...
using value_type = typename listmap<Key,Value,Less>::value_type;
};
allows you to write the definition more succinctly using auto suffix type:
template <typename Key, typename Value, class Less>
auto listmap<Key,Value,Less>::iterator::operator*() -> value_type&
{
return where->value;
}
Careful: inner iterator class is not template, only listmap is:
listmap<Key,Value,Less>::iterator<Key,Value,Less>::operator
// ~~~~~~~~~~~~~~~~ remove this
1 Btw don't forget the others.

Sharing template struct among templates

In my everlasting effort to implement a link list I created a class-template representing the container (CursorList) and I have a seperate class-template that is supposed to be my iterator (CursorIterator).
In my CursorList I use a struct Node to represent elements of the list. I want to share this struct with my CursorIterator-Class (the iterator is pointing towards a Node). However this doesn't work as I want it to, I can't really get both classes to know the structures of Node.
CursorList.h
#ifndef CURSORLIST_H
#define CURSORLIST_H
#include "CursorIterator.h"
template <class T> class CursorList {
public:
CursorList() {}
typedef T value_type;
typedef CursorIterator<T> iterator;
bool empty() const;
int size() const;
T& front() const;
void push_front(const T& item);
void pop_front();
iterator begin() const;
iterator end() const;
iterator insert(iterator itr, const T& value);
iterator erase(iterator start, iterator stop);
iterator erase(iterator itr);
struct Node {
Node(const T& n_data, Node* n_prev, Node* n_next): data(n_data), prev(n_prev), next(n_next) {}
T data;
Node* prev;
Node* next;
};
private:
Node* m_head;
};
#endif //CURSORLIST_H
CursorIterator.h
#ifndef CURSORITERATOR_H
#define CURSORITERATOR_H
template <class T> class CursorIterator {
private:
typedef CursorIterator<T> iterator;
Node* m_rep;
public:
CursorIterator() {}
CursorIterator(Node* n): m_rep(n) {}
T& operator *();
iterator& operator = (const iterator& rhs);
bool operator != (const iterator& rhs) const;
bool operator == (const iterator& rhs) const;
iterator& operator ++();
iterator operator ++(int);
};
#endif //CURSORITERATOR_H

Best way to encapsulate the implementation using iterators

I am developping a Grpah class using a QList of nodes. Each node contains a QList of adjacent.
It is sure useful to be able to loop through the nodes of the graph and through the neighborhood of every node, but it seems that having a function QList<Node*> getNodes and QList<Node*> getAdjacentwould not be very smart (because I would show that I use a QList).
So I decided to have an iterator for eah of my classes. Very simple implementation:
template<class T>
class Graph {
private:
QList< Node<T>* > _nodes;
public:
typedef typename QList< Node<T>* >::const_iterator const_iterator;
typedef typename QList< Node<T>* >::iterator iterator;
iterator begin() {return _nodes.begin();}
const_iterator begin() const {return _nodes.begin();}
const_iterator cbegin() const {return _nodes.cbegin();}
const_iterator cend() const {return _nodes.cend();}
const_iterator constBegin() const {return _nodes.constBegin();}
const_iterator constEnd() const {return _nodes.constEnd();}
iterator end() {return _nodes.end();}
const_iterator end() const {return _nodes.end();}
Graph();
/*other methods*/
};
But then I have the exact same code for my Node template class. That code works, and seems to me a good idea.
So I id something a little bit messy:
template<class T>
class Iterable {
protected:
T* _data;
public:
Iterable() {_data = new T();}
typedef typename T::const_iterator const_iterator;
typedef typename T::iterator iterator;
iterator begin() {return _data->begin();}
const_iterator begin() const {return _data->begin();}
const_iterator cbegin() const {return _data->cbegin();}
const_iterator cend() const {return _data->cend();}
const_iterator constBegin() const {return _data->constBegin();}
const_iterator constEnd() const {return _data->constEnd();}
iterator end() {return _data->end();}
const_iterator end() const {return _data->end();}
};
and then:
template<class T>
class Node : public Iterable< QList< Node<T>* > >{/*code*/};
template<class T>
class Graph : public Iterable< QList< Node<T>* > >{/*code*/};
Is this the best way to do this? Because it looks nasty, although its completely refactored...
This part is a duplicate question from. As other users pointed out in the comments, I shall stick to one question: the one above this.
*Also, now I need to type typenamewhen trying to get an interator type inside Graph or Node, but not outside. That is to say:
template
void Graph::example() const {
typename Graph::const_iterator it;
//If i remove typename I get: error: need ‘typename’ before ‘Graph::const_iterator’ because ‘Graph’ is a dependent scope
}
whereas outside of the class I can do:
Graph::iterator it = g.begin();
Why is this?*
Thanks!