iterators implementation in STL containers - c++

This is the sample code I've written, just a simple version to test out an iterator implementation:
template <typename T>
struct ArrayIterator {
using value_type = typename T::value_type;
using pointer = value_type*;
using reference = value_type&;
using self = ArrayIterator;
pointer ptr;
ArrayIterator() : ptr{nullptr} {}
ArrayIterator(pointer ptr) : ptr{ptr} {}
reference operator*() {
return *ptr;
}
bool operator==(self const& other) const {
return ptr == other.ptr;
}
bool operator!=(self const& other) const {
return !(*this == other);
}
self operator++() {
++ptr;
return *this;
}
self operator++(int) {
auto copy = *this;
++ptr;
return copy;
}
};
template <typename T, size_t size>
struct Array {
T arr[size];
using value_type = T;
using iterator = ArrayIterator<Array<T, size>>;
iterator begin() {
return iterator(arr);
}
Array() : arr{1, 3, 4, 22, 3} {}
iterator end() {
return iterator(arr + size);
}
};
int main() {
using array = Array<int, 5>;
array arr;
}
Assuming everything is public, and we only pass int as type parameter, and the array has the default constructor just to test range-based for loop. It works.
My question is, why can't we use inheritance? Something like this:
template <typename T, size_t size>
struct Array : ArrayIterator<Array<T, size>> {}
If used, I get the following error (compiler is clang 11):
first.cpp:46:34: error: no type named 'value_type' in 'Array<int, 5>'
using value_type = typename T::value_type;
~~~~~~~~~~~~^~~~~~~~~~
first.cpp:82:16: note: in instantiation of template class 'ArrayIterator<Array<int, 5>>' requested here
struct Array : ArrayIterator<Array<T, size>> {
^
first.cpp:101:9: note: in instantiation of template class 'Array<int, 5>' requested here
array arr;
^
1 error generated
I can’t understand what's going on.

You can’t use inheritance like you want because the code will have a catch-22 if you try. ArrayIterator will try to define its value_type as an alias of the array’s value_type before the latter has been defined yet. That is why you are getting the compiler error.
Your iterator has no need to access the Array class itself, so there is no point in passing the Array class type to the iterator’s template parameter. Just pass the array’s value_type instead of Array itself, and change the iterator’s value_type to be just T instead of typename T::value_type:
template <typename T>
struct ArrayIterator {
using value_type = T;
using pointer = value_type*;
using reference = value_type&;
using self = ArrayIterator;
pointer ptr;
ArrayIterator() : ptr{nullptr} {}
ArrayIterator(pointer ptr) : ptr{ptr} {}
reference operator*() {
return *ptr;
}
bool operator==(self const& other) const {
return ptr == other.ptr;
}
bool operator!=(self const& other) const {
return ptr != other.ptr;
}
self& operator++() {
++ptr;
return *this;
}
self operator++(int) {
auto copy = *this;
++ptr;
return copy;
}
};
template <typename T, size_t size>
struct Array {
using value_type = T;
using iterator = ArrayIterator<T>;
T arr[size];
iterator begin() {
return iterator(arr);
}
iterator end() {
return iterator(arr + size);
}
};
int main() {
using array = Array<int, 5>;
array arr{1, 3, 4, 22, 3};
for (auto val : arr) {
cout << val << endl;
}
}

Related

deduced class type ‘Iterator’ in function return type

Newbie in programming.
#include <iostream>
template<typename T>
class Container {
protected:
size_t size_;
T* arr_;
public:
using value_type = T;
using size_type = size_t;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
template<bool IsConst>
struct Iterator {
public:
std::conditional_t<IsConst, const T*, T*> ptr_;
Iterator(): ptr_(nullptr) {}
Iterator(std::conditional_t<IsConst, const T*, T*> ptr): ptr_(ptr) {}
std::conditional_t<IsConst, const T&, T&> operator*() const { return *ptr_; }
};
using iterator = Iterator<false>;
using const_iterator = Iterator<true>;
public:
size_type size() const { return size_; }
Iterator begin() const { return Iterator(this->arr_); }
};
How to overload the method in this case
Iterator begin() const { return Iterator(this->arr_); }
I inherit from my vector and want to refer to my objects as in the example.
void main() {
Vector<double> v = {66, 22, 33, 6, 7};
const Vector<double> v2 = {66, 22, 33, 6, 7};
// First case
Vector<double>::iterator itr = v.begin();
std::cout << *itr << std::endl;
// Second case
const Vector<double>::const_iterator itr2 = v2.begin();
std::cout << *itr2 << std::endl;
}
If I use "using" in a method like this
const_iterator begin() const { return const_terator(this->arr_); }
The second case will work.
How to overload the method or is there another way to make both cases work?

Using std::conditional with iterator

In "Mastering the C++17 STL" book I saw both iterator and const_iterator implementation in one class using conditional for less code duplication
Here's my implementation for simple array class (most code for array class is skipped):
template<class T, size_t N>
class Array
{
public:
template<bool Const>
class ArrayIterator {
friend class Array;
public:
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = std::conditional<Const, const value_type*, value_type*>;
using reference = std::conditional<Const, const value_type&, value_type&>;
using iterator_category = std::random_access_iterator_tag;
reference operator*() const { return *ptr; }
ArrayIterator<Const>& operator++() { ++ptr; return *this; }
ArrayIterator<Const> operator++(int) { auto res = *this; ++(*this); return res; }
template<bool R>
bool operator==(const ArrayIterator<R>& iter) const { return ptr == iter.ptr; }
template<bool R>
bool operator!=(const ArrayIterator<R>& iter) const { return ptr != iter.ptr; }
private:
explicit ArrayIterator(pointer p) : ptr(p) {};
pointer ptr;
};
using iterator = ArrayIterator<false>;
using const_iterator = ArrayIterator<true>;
iterator begin() { return iterator(data); }
iterator end() { return iterator(data + N); }
const_iterator cbegin() const { return const_iterator(data); }
const_iterator cend() const { return const_iterator(data + N); }
private:
T* data;
};
This code compiles with no errors, but iterator is kinda unusable:
Array<int, 100> arr;
/*filling it with numbers*/
int x = *arr.begin();
Gives error:
main.cpp:9:9: error: no viable conversion from 'Array<int, 100>::ArrayIterator<false>::reference' (aka 'conditional<false, const int &, int &>') to 'int'
How can I use that iterator or should I just abandon this idea from book?
The member type pointer and reference of ArrayIterator should be defined as member type type of std::conditional, not std::conditional itself.
Change them to:
using pointer = typename std::conditional<Const, const value_type*, value_type*>::type;
// ^^^^^^^^ ^^^^^^
using reference = typename std::conditional<Const, const value_type&, value_type&>::type;
// ^^^^^^^^ ^^^^^^
Or (since C++14)
using pointer = std::conditional_t<Const, const value_type*, value_type*>;
// ^^
using reference = std::conditional_t<Const, const value_type&, value_type&>;
// ^^

Custom container list: problem with compiling from iterator to const_iterator

I am trying to recreate some of the C++ containers for a school project and for that I had to also implement iterators. I am currently working on the List container and I am facing a conversion problem.
Here are the parts of the code that are involved:
I have an Elem structure (corresponding to 1 element of a doubly linked list that I use for my List container)
template <class T>
struct Elem
{
Elem *prev;
T data;
Elem *next;
};
a BidirectionalIterator class (used for the list iterators). Here are the constructors:
template <class T>
class BidirectionalIterator
{
public:
typedef BidirectionalIterator iterator;
typedef T value_type;
typedef size_t size_type;
BidirectionalIterator() { _ptr = nullptr; };
BidirectionalIterator(Elem<value_type> *ptr) {
*this->_ptr = ptr;
};
BidirectionalIterator(const iterator &x) {
*this->_ptr = x._ptr;
};
~BidirectionalIterator() {};
iterator &operator=(const iterator &x) {
*this->_ptr = x._ptr;
return (*this);
};
[...]
};
and my list class:
template <class T, class Alloc = std::allocator<T>>
class list
{
public:
typedef T value_type;
typedef BidirectionalIterator<T> iterator;
typedef BidirectionalIterator<const T> const_iterator;
typedef size_t size_type;
/* CONSTRUCTORS */
[...]
list(const list &x) {
_init_list();
assign(x.begin(), x.end());
};
/* ITERATORS */
iterator begin() {
return (iterator(_start));
};
const_iterator begin() const {
return (const_iterator(_start));
};
iterator end() {
return (iterator(_tail));
};
const_iterator end() const {
return (const_iterator(_tail));
};
/* ASSIGN */
void assign(iterator first, iterator last);
void assign(const_iterator first, const_iterator last);
[...]
private:
Elem<value_type> *_head;
Elem<value_type> *_start;
Elem<value_type> *_end;
Elem<value_type> *_tail;
[...]
};
In my main program I' m just calling a function (T being an int) that implicitely calls the copy constructor:
void print_content(ft::list<T> lst);
But when I compile i get this:
./List.hpp:71:12: error: no matching conversion for functional-style cast from 'Elem<ft::list<int, std::allocator<int>
>::value_type> *const' (aka 'Elem<int> *const') to 'ft::list<int, std::allocator<int> >::const_iterator' (aka
'BidirectionalIterator<const int>')
return (const_iterator(_start));
^~~~~~~~~~~~~~~~~~~~~
./List.hpp:53:13: note: in instantiation of member function 'ft::list<int, std::allocator<int> >::begin' requested
here
assign(x.begin(), x.end());
./../Iterator/BidirectionalIterator.hpp:45:3: note: candidate constructor not viable: no known conversion from
'Elem<ft::list<int, std::allocator<int> >::value_type> *const' (aka 'Elem<int> *const') to
'Elem<ft::BidirectionalIterator<const int>::value_type> *' (aka 'Elem<const int> *') for 1st argument
BidirectionalIterator(Elem<value_type> *ptr) {
I don't know how to fix that problem. I already tried to delete the const attribute from my copy constructor and it works, but it needs to be const (for the rest of my project cause I'm implementing the relational operators that call a const list, and also to respect the original container constructor).
Does anyone have an idea?
You try to create an Elem<const int>* from an Elem<int> *const.
I suggest making the iterator's pointer Elem<std::remove_const_t<T>>* (even for a const_iterator) but let dereferencing a const_iterator return a T const& or T const *.
Example:
template <class T>
class BidirectionalIterator {
public:
using value_type = T;
using reference = value_type&;
using pointer = value_type*;
using size_type = std::size_t;
BidirectionalIterator() : _ptr(nullptr) {};
BidirectionalIterator(Elem<std::remove_const_t<value_type>>* ptr) : _ptr(ptr) {};
BidirectionalIterator(const BidirectionalIterator& x) {
_ptr = x._ptr;
};
BidirectionalIterator& operator=(const BidirectionalIterator& x) {
_ptr = x._ptr;
return *this;
};
reference operator*() const { return _ptr->data; }
pointer operator->() const { return &_ptr->data; }
Elem<std::remove_const_t<value_type>>* _ptr;
};
A slightly better version to let you create lists of const Ts and to also let you convert iterators to const_iterators (but not the other way around) to be able to compare iterators could look like this:
#include <memory>
#include <type_traits>
template <class T, class ElemType> // const or non-const T and the type used in Elem
class BidirectionalIterator {
public:
using value_type = T;
using reference = value_type&;
using pointer = value_type*;
using size_type = std::size_t;
BidirectionalIterator() : _ptr(nullptr) {};
BidirectionalIterator(Elem<ElemType>* ptr) : _ptr(ptr) {};
// let a conversion constructor of the const_iterator read _ptr
friend class BidirectionalIterator<const ElemType, ElemType>;
// enable a const_iterator to be created from a non-const iterator via
// a conversion constructor
template<typename U = T, typename V = ElemType,
std::enable_if_t<std::is_const_v<U>&&!std::is_const_v<V>, int> = 0
>
BidirectionalIterator(const BidirectionalIterator<ElemType, ElemType>& x) :
_ptr(x._ptr) {}
// normal copy ctor
BidirectionalIterator(const BidirectionalIterator& x) : _ptr(x._ptr) {}
BidirectionalIterator& operator=(const BidirectionalIterator& x) {
_ptr = x._ptr;
return *this;
};
// the conversion constructor lets you compare a const_iterator and an iterator
bool operator==(const BidirectionalIterator& rhs) const {
return _ptr == rhs._ptr;
}
bool operator!=(const BidirectionalIterator& rhs) const {
return !(_ptr == rhs._ptr);
}
reference operator*() const { return _ptr->data; }
pointer operator->() const { return &_ptr->data; }
private:
Elem<ElemType>* _ptr;
};
// iterator == const_iterator, swap order to use member operator==
template<typename T>
bool operator==(const BidirectionalIterator<T, T>& a,
const BidirectionalIterator<const T, T>& b) {
return b == a;
}
// iterator != const_iterator, swap order to use member operator!=
template<typename T>
bool operator!=(const BidirectionalIterator<T, T>& a,
const BidirectionalIterator<const T, T>& b) {
return b != a;
}
With this iterator definition, you'd need to define your iterator and const_iterator slightly different.
template <class T, class Alloc = std::allocator<T>>
class list {
public:
using value_type = T;
using iterator = BidirectionalIterator<T, T>;
using const_iterator = BidirectionalIterator<const T, T>;
//...

Iterators with complex value type: confusion with value_type and reference

I want to create a custom iterator wrapper, for example, enumerate: given a pair of iterators over type T, it would return an iterable over type std::pair<const int, T&>, where the first element of the pair will take values 0, 1, 2, so on.
I have a problem figuring out what should be value_type and reference of my iterator. I want to support two behaviours:
First, referencing values of the underlying sequence:
for (auto& kv: enumerate(my_vec)) {
kv.second = kv.first;
}
(sort of std::iota);
Second, making a copy of the value:
std::vector<int> a{10, 20, 30};
auto copy = *enumerate(a).begin();
a[0] = 15;
std::cout << copy.first << " " << copy.second; // 0 10
I'm confused what should be the return type of Iterator::operator*(). If it is std::pair<const int, T&> then in the second example value will not be copied. If it is std::pair<const int, T> then in the first example it is impossible to reference underlying values. What should I do and what should be value_type, reference and pointer typedefs of such iterator?
Here is my attempt to implement it. It supports taking references but not copying.
template<typename T>
struct Iterator {
using TT = typename std::iterator_traits<T>::value_type;
using value_type = std::pair<const int, TT>;
using reference = std::pair<const int&, typename std::iterator_traits<T>::reference>;
using pointer = value_type*;
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
std::pair<int, T> it;
Iterator(T iterator) : it(0, iterator) {}
bool operator==(const Iterator& other) const { return it.second == other.it.second; }
bool operator!=(const Iterator& other) const { return it.second != other.it.second; }
reference operator*() { return { it.first, *it.second }; }
Iterator& operator++() { ++it.first; ++it.second; return *this; }
};
P.S. I've just checked, boost::adaptors::index suffers from the same problem and does not copy the value.
This problem is similar to that of std::vector<bool>, you want to provide a proxy that acts just like a reference but also supports value semantics.
What's different though, is that the types involved aren't restricted, there is two references involved and all sorts of hairiness pops up. The following is a partial implementation and it illustrates some problems you run into
#include<iterator>
#include<functional>
template<typename F, typename S, bool defined = true>
struct sfinae_difference_type {};
template<typename F, typename S>
struct sfinae_difference_type<F, S,
std::is_same_v<typename std::iterator_traits<F>::difference_type,
typename std::iterator_traits<S>::difference_type>>
{
using difference_type = typename std::iterator_traits<F>::difference_type;
};
template<typename F, typename S>
class pair_iterator : sfinae_difference_type<F, S>
{
using Fvalue_type = typename std::iterator_traits<F>::value_type;
using Svalue_type = typename std::iterator_traits<S>::value_type;
using Freference = typename std::iterator_traits<F>::reference;
using Sreference = typename std::iterator_traits<S>::reference;
F f;
S s;
public:
using value_type = std::pair<Fvalue_type, Svalue_type>;
struct reference
{
Freference first;
Sreference second;
reference() = delete;
reference(const reference& other) : first{other.first}, second{other.second} {}
reference& operator=(const reference& rhs)
{
first = rhs.first;
second = rhs.second;
return *this;
}
operator value_type() { return {f, s}; }
private:
reference(Freference f, Sreference s) : first{f}, second{s} {}
friend pair_iterator;
};
struct pointer
{
// similar to reference
};
pair_iterator() = default;
pair_iterator(const pair_iterator&) = default;
pair_iterator(F f, S s) : f{f}, s{s} {}
pair_iterator& operator++() { ++f; ++s; return *this; }
reference operator*() { return {*f, *s}; }
pointer operator->() { return {f.operator->(), s.operator->()}; }
bool operator==(const pair_iterator& other)
{
return f == other.f && s == other.s;
}
};
You then use it as
#include<vector>
#include<list>
#include<iostream>
int main()
{
std::vector v{1, 2, 3, 4, 5};
std::list l{6, 7, 8, 9, 10};
pair_iterator begin{v.begin(), l.begin()}, end{v.end(), l.end()};
for(; begin != end; ++begin)
std::cout << begin->first << ' ' << begin->second << '\n';
}
Live
Some of the immediately obvious problems:
Implementation is tedious. Having sfinae friendly type aliases and proper proxies requires copious boilerplate.
The semantics of proxies may be confusing. What does copying/assigning one reference to another mean? What is auto is_this_a_copy = *it supposed to do?
What does equality mean? Does both internal iterators have to be equal to be equal? That breaks comparison with end iterators.
All of these have to be hammered out to make it work, and there isn't an easy answer.

How to define a 3d array with each dimension a different type in C++?

I want to define a 3D array like this:
Type ary[3/*enumeration type*/][6/*int*/][7/*const wchar**/];
Is it possible in C++? I'm using Visual Studio 2010, Boost library is not allowed. If it's possible, please tell me how to initialize each dimension?
Following may help you:
template <typename T, std::size_t N, typename IndexType>
class typed_array
{
public:
typedef typename std::array<T, N>::const_iterator const_iterator;
typedef typename std::array<T, N>::iterator iterator;
public:
const T& operator [] (IndexType index) const { return array[int(index)]; }
T& operator [] (IndexType index) { return array[int(index)]; }
// discard other index type
template <typename U> const T& operator [] (U&&) const = delete;
template <typename U> T& operator [] (U&&) = delete;
const_iterator begin() const { return array.begin(); }
const_iterator end() const { return array.end(); }
iterator begin() { return array.begin(); }
iterator end() { return array.end(); }
private:
std::array<T, N> array;
};
enum class E { A, B, C };
int main(int argc, char *argv[])
{
typed_array<int, 3, E> a;
typed_array<typed_array<int, 4, char>, 3, E> b;
//a[2] = 42; // doesn't compile as expected. `2` is not a `E`
b[E::A]['\0'] = 42;
//b[E::A][2] = 42; // doesn't compile as expected. `2` is not a `char`
return 0;
}
It is impossible as it is because it is array elements of the same type.
struct s_i {
int i;
const wchar *wc[7];
};
struct s_e {
enum Ex e;
struct s_i i[6];
} ary[3];
No it's not possible.
What you can to is to make a class that behaves as an enumeration and has a subscript operator which returns another special class (that in general behaves as an int) which has a subscript operator which in turn returns the wchar_t array.