What is the complete interface of a C++ standard compatible library container? - c++

I'm trying to understand the C++ standard library. If I want to build a new kind of container fully compatible with latest C++17 standards (excluding polymorphic allocator support as I'm not that clear on that yet), with full compatibility with iterators, what is the complete set of member functions and types I must provide?
Take a sequence container like std::vector for example. In other words, say I want to provide everything that std::vector provides. Also considering allocator support.
Of course I also want to provide iterators, exactly as std::vector does it (I don't want to delegate to it; I want from the ground up remake), so I suppose I will need two nested iterator classes, one for iterator and one for const_iterator.
I've come up with the following:
template <class T, class Alloc = std::allocator_traits<T>>
class StdLibClass
{
public:
using allocator_type = Alloc;
using value_type = typename Alloc::value_type;
using reference = typename Alloc::reference;
using const_reference = typename Alloc::const_reference;
using difference_type = typename Alloc::difference_type;
using size_type = typename Alloc::size_type;
class Iterator
{
public:
using difference_type = typename Alloc::difference_type;
using value_type = typename Alloc::value_type;
using reference = typename Alloc::reference;
using pointer = typename Alloc::pointer;
using iterator_category = std::random_access_iterator_tag; // or another Iterator tag you want
Iterator();
Iterator(const Iterator&);
~Iterator();
Iterator& operator=(const Iterator&);
bool operator==(const Iterator&) const;
bool operator!=(const Iterator&) const;
bool operator<(const Iterator&) const;
bool operator>(const Iterator&) const;
bool operator<=(const Iterator&) const;
bool operator>=(const Iterator&) const;
Iterator &operator++();
Iterator operator++(int);
Iterator &operator--();
Iterator operator--(int);
Iterator &operator+=(size_type);
Iterator operator+(size_type) const;
friend Iterator operator+(size_type, const Iterator&);
Iterator &operator-=(size_type);
Iterator operator-(size_type) const;
difference_type operator-(Iterator) const;
reference operator*() const;
pointer operator->() const;
reference operator[](size_type) const;
};
class ConstIterator
{
public:
using difference_type = typename Alloc::difference_type;
using value_type = typename Alloc::value_type;
using reference = typename const Alloc::reference;
using pointer = typename const Alloc::pointer;
using iterator_category = std::random_access_iterator_tag; // or another Iterator tag you want
ConstIterator();
ConstIterator(const ConstIterator&);
ConstIterator(const Iterator&);
~ConstIterator();
ConstIterator& operator=(const ConstIterator&);
bool operator==(const ConstIterator&) const;
bool operator!=(const ConstIterator&) const;
bool operator<(const ConstIterator&) const;
bool operator>(const ConstIterator&) const;
bool operator<=(const ConstIterator&) const;
bool operator>=(const ConstIterator&) const;
ConstIterator &operator++();
ConstIterator operator++(int);
ConstIterator &operator--();
ConstIterator operator--(int);
ConstIterator &operator+=(size_type);
ConstIterator operator+(size_type) const;
friend ConstIterator operator+(size_type, const ConstIterator&);
ConstIterator &operator-=(size_type);
ConstIterator operator-(size_type) const;
difference_type operator-(ConstIterator) const;
reference operator*() const;
pointer operator->() const;
reference operator[](size_type) const;
};
using ReverseIterator = std::reverse_iterator<Iterator>;
using ConstReverseIterator = std::const_reverse_iterator<ConstIterator>;
StdLibClass();
~StdLibClass();
StdLibClass(const StdLibClass&);
StdLibClass& operator=(const StdLibClass&);
StdLibClass(const StdLibClass&&);
StdLibClass& operator=(const StdLibClass&&);
bool operator==(const StdLibClass&) const;
bool operator!=(const StdLibClass&) const;
bool operator<(const StdLibClass&) const;
bool operator>(const StdLibClass&) const;
bool operator<=(const StdLibClass&) const;
bool operator>=(const StdLibClass&) const;
Iterator begin();
ConstIterator begin() const;
ConstIterator cbegin() const;
Iterator end();
ConstIterator end() const;
ConstIterator cend() const;
ReverseIterator rbegin();
ConstReverseIterator rbegin() const;
ConstReverseIterator crbegin() const;
ReverseIterator rend();
ConstReverseIterator rend() const;
ConstReverseIterator crend() const;
reference front();
const_reference front() const;
reference back();
const_reference back() const;
template <class... TArgs>
void emplace_front(TArgs &&...);
template <class... TArgs>
void emplace_back(TArgs &&...);
void push_front(const T&);
void push_front(T&&);
void push_back(const T&);
void push_back(T&&);
void pop_front();
void pop_back();
reference operator[](size_type);
const_reference operator[](size_type) const;
reference at(size_type);
const_reference at(size_type) const;
template <class... TArgs>
Iterator emplace(ConstIterator, TArgs&&...);
Iterator insert(ConstIterator, const T&);
Iterator insert(ConstIterator, T&&);
Iterator insert(ConstIterator, size_type, T&);
template <class Iter>
Iterator insert(ConstIterator, Iter, Iter);
Iterator insert(ConstIterator, std::initializer_list<T>);
Iterator erase(ConstIterator);
Iterator erase(ConstIterator, ConstIterator);
void clear();
template <class Iter>
void assign(Iter, Iter);
void assign(std::initializer_list<T>);
void assign(size_type, const T&);
void swap(StdLibClass &);
size_type size() const;
size_type max_size() const;
bool empty() const;
Alloc get_allocator() const;
};
// possibly want to specialize std::swap for the class too
namespace std
{
template <>
void swap<T, StdLibClass<T, Alloc>>(StdLibClass<T, Alloc>&, StdLibClass<T, Alloc>&);
} // namespace std
int main()
{
return 0;
}
Some I've found in webpages around the internet, others I've found by digging the standard library headers. Is there some member or some other concept I'm missing?

I think, depending on the kind of container you want to implement, the new standard requirements (standard concepts) added in C++20 are a good place to look for the required interface of a standard container.
Example for Container and especially AllocatorAwareContainer

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.

C++ Custom Iterator

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?

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

How can I implement various iterator categories in an elegant and efficient way?

I'm implementing STL containers, for example, vector. What confused me is the implementation of iterators.
If I want to implement all iterator categories: input_iterator, output_iterator, forward_iterator, bidirectional_iterator and random_access_iterator.
How do I manage their inheritance relationships?
I've read How to implement an STL-style iterator and avoid common pitfalls?-Mooing Duck's Answer
This is his example symbolic:
iterator {
iterator(const iterator&);
~iterator();
iterator& operator=(const iterator&);
iterator& operator++(); //prefix increment
reference operator*() const;
friend void swap(iterator& lhs, iterator& rhs); //C++11 I think
};
input_iterator : public virtual iterator {
iterator operator++(int); //postfix increment
value_type operator*() const;
pointer operator->() const;
friend bool operator==(const iterator&, const iterator&);
friend bool operator!=(const iterator&, const iterator&);
};
//once an input iterator has been dereferenced, it is
//undefined to dereference one before that.
output_iterator : public virtual iterator {
reference operator*() const;
iterator operator++(int); //postfix increment
};
//dereferences may only be on the left side of an assignment
//once an input iterator has been dereferenced, it is
//undefined to dereference one before that.
forward_iterator : input_iterator, output_iterator {
forward_iterator();
};
//multiple passes allowed
bidirectional_iterator : forward_iterator {
iterator& operator--(); //prefix increment
iterator operator--(int); //postfix decrement
};
random_access_iterator : bidirectional_iterator {
friend bool operator<(const iterator&, const iterator&);
friend bool operator>(const iterator&, const iterator&);
friend bool operator<=(const iterator&, const iterator&);
friend bool operator>=(const iterator&, const iterator&);
iterator& operator+=(size_type);
friend iterator operator+(const iterator&, size_type);
friend iterator operator+(size_type, const iterator&);
iterator& operator-=(size_type);
friend iterator operator-(const iterator&, size_type);
friend difference_type operator-(iterator, iterator);
reference operator[](size_type) const;
};
But I found a problem:
If I have an instance a from class random_access_iterator, I use the code random_access_iterator b = a + 1. This will cause compile error. Because a + 1's class is base iterator, not random_access_iterator.
So I don't think this is a reasonable solution.
Do I misunderstand it? Or please tell me an elegant and efficient way to implement it.
Thanks
I think you should use CRTP (https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern).
Like this:
template <typename T, typename ItT>
struct iiterator_t {
typedef T value_type;
typedef T& reference;
typedef T* pointer;
typedef ItT iterator_type;
virtual iterator_type& operator=(const iterator_type&) = 0;
virtual iterator_type& operator++() = 0;
virtual reference operator*() const = 0;
};
template <typename T, typename ItT>
struct iterator_impl_t : virtual public iiterator_t<T, ItT>{
typedef T value_type;
typedef T& reference;
typedef T* pointer;
typedef ItT iterator_type;
iterator_type& operator=(const iterator_type &rhs)
{
p = static_cast<const iterator_impl_t&>(rhs).p;
return dynamic_cast<iterator_type&>(*this);
}
iterator_type& operator++()
{
++p;
return dynamic_cast<iterator_type&>(*this);
}
reference operator*() const
{
return *p;
}
private:
pointer p;
};
template <typename T, typename ItT>
struct iinput_iterator_t : public virtual iiterator_t<T, ItT> {
typedef T value_type;
typedef T& reference;
typedef T* pointer;
typedef ItT iterator_type;
virtual iterator_type operator++(int) = 0;
};
template <typename T, typename ItT>
struct input_iterator_impl_t :
public virtual iinput_iterator_t<T, ItT>,
public virtual iterator_impl_t<T, ItT>
{
typedef T value_type;
typedef T& reference;
typedef T* pointer;
typedef ItT iterator_type;
iterator_type operator++(int)
{
iterator_type result(dynamic_cast<const iterator_type &>(*this));
++dynamic_cast<iterator_impl_t<T, ItT> &>(*this);
return result;
}
};
template <typename T>
struct iterator :
public virtual iterator_impl_t<T, iterator<T> >
{
};
template <typename T>
struct input_iterator :
public virtual input_iterator_impl_t<T, input_iterator<T>>
{
};
int main(int , char** )
{
iterator<int> i;
iterator<int> i2 = ++i;
input_iterator<int> inpi;
input_iterator<int> inpi2 = inpi++;
return 0;
}
Iterator categories are nothing but empty structs that act as tags.
Once finished implementing the features of your iterator class, you add its information (like which category it belongs to) into a std::iterator_traits specialization. Here's an example:
namespace std {
template <typename T>
struct iterator_traits<my_iota_iterator<T>> {
using value_type = T;
using difference_type = T;
using pointer = T const*;
using reference = T const&;
using iterator_category = std::random_access_iterator_tag; // !!
} /*struct iterator_traits*/;
} /*namespace std*/;
You can also choose to place these type aliases directly in the iterator class itself. Regardless, algorithms can now specialize upon the type of iterator_category and implement specific versions for specific categories.

writing a map as template with Pair and iterator c++

A little help please with this task, I need to write a simplified map template with Pair class and iterator class, I'm a beginner and I'm really messy and troubled with this, I would be glad for some help only with the header.
#include <cassert>
#include <functional>
#include "Exceptions.h"
template <typename DataType>
struct Node {
DataType data;
Node<DataType> *next;
};
namespace mtm {
template <class KeyType, class ValueType, class CompareFunction = std::less<KeyType> >
class MtmMap {
public:
class Pair {
public:
Pair(const KeyType& key, const ValueType& value)
: first(key), second(value){}
const KeyType first;
ValueType second;
~Pair() = default;
Pair* begin();
Pair* end();
void insert();
void insert(const KeyType&, const ValueType&);
void remove(const KeyType&);
bool containsKey(const KeyType& key) const;
int size() const;
Pair& operator=(const Pair&) = default;
ValueType& operator[](const KeyType&);
const ValueType& operator[](const KeyType&) const;
Pair& operator[]();
/*private:
Node<Pair> _pairs;
int _size;
*/
};
class iterator{
/*private:
Pair *cur_itr;
Node<Pair*> pair_ptr;
*/
public:
iterator();
iterator(const Pair*);
iterator(const iterator&);
~iterator();
iterator& operator=(const iterator&) = default;
iterator& operator++(int);
iterator& operator++();
iterator& operator*();
bool operator==(const iterator&);
};
bool operator!=(const iterator&, const iterator&);
};
}
I was given an empty part, like this:
#include <cassert>
#include <functional>
#include "Exceptions.h"
namespace mtm {
template <class KeyType, class ValueType, class CompareFunction = std::less<KeyType> >
class MtmMap {
public:
class Pair {
public:
Pair(const KeyType& key, const ValueType& value)
: first(key), second(value) {}
const KeyType first;
ValueType second;
};
};
}
And the first part is after I wrote all the needed for realization operators and functions. Notice that it is pretty simplified version of map container, so I only need this part, WITHOUT USING ANY STL.
Can somebody help with writing the private fields and other things that belongs to the structure of the template class MtmMap ???
The idea using a list struct is mine, as I need list of operators and pairs as a container, as I can see it.
Any other ideas, comments, and stuff will be excellent and I'll be grateful, thanks a lot.
EDIT:
Just to clarify my question, before I'll continue to the cpp file for realization, I'm stuck, because I don't understand how I should realize all the functions of the map, how to storage them and etc.
I need help finishing this header.