Preparation for std::iterator Being Deprecated - c++

On March 21st the standards committee voted to approve the deprecation of std::iterator proposed in P0174:
The long sequence of void arguments is much less clear to the reader than simply providing the expected typedefs in the class definition itself, which is the approach taken by the current working draft, following the pattern set in c++14
Before c++17 inheritance from std::iterator was encouraged to remove the tedium from iterator boilerplate implementation. But the deprecation will require one of these things:
An iterator boilerplate will now need to include all required typedefs
Algorithms working with iterators will now need to use auto rather than depending upon the iterator to declare types
Loki Astari has suggested that std::iterator_traits may be updated to work without inheriting from std::iterator
Can someone enlighten me on which of these options I should expect, as I design custom iterators with an eye towards c++17 compatibility?

The discussed alternatives are clear but I feel that a code example is needed.
Given that there will not be a language substitute and without relying on boost or on your own version of iterator base class, the following code that uses std::iterator will be fixed to the code underneath.
With std::iterator
template<long FROM, long TO>
class Range {
public:
// member typedefs provided through inheriting from std::iterator
class iterator: public std::iterator<
std::forward_iterator_tag, // iterator_category
long, // value_type
long, // difference_type
const long*, // pointer
const long& // reference
> {
long num = FROM;
public:
iterator(long _num = 0) : num(_num) {}
iterator& operator++() {num = TO >= FROM ? num + 1: num - 1; return *this;}
iterator operator++(int) {iterator retval = *this; ++(*this); return retval;}
bool operator==(iterator other) const {return num == other.num;}
bool operator!=(iterator other) const {return !(*this == other);}
long operator*() {return num;}
};
iterator begin() {return FROM;}
iterator end() {return TO >= FROM? TO+1 : TO-1;}
};
(Code from http://en.cppreference.com/w/cpp/iterator/iterator with original author's permission).
Without std::iterator
template<long FROM, long TO>
class Range {
public:
class iterator {
long num = FROM;
public:
iterator(long _num = 0) : num(_num) {}
iterator& operator++() {num = TO >= FROM ? num + 1: num - 1; return *this;}
iterator operator++(int) {iterator retval = *this; ++(*this); return retval;}
bool operator==(iterator other) const {return num == other.num;}
bool operator!=(iterator other) const {return !(*this == other);}
long operator*() {return num;}
// iterator traits
using difference_type = long;
using value_type = long;
using pointer = const long*;
using reference = const long&;
using iterator_category = std::forward_iterator_tag;
};
iterator begin() {return FROM;}
iterator end() {return TO >= FROM? TO+1 : TO-1;}
};

Option 3 is a strictly more-typing version of Option 1, since you have to write all the same typedefs but additionally wrap iterator_traits<X>.
Option 2 is unviable as a solution. You can deduce some types (e.g. reference is just decltype(*it)), but you cannot deduce iterator_category. You cannot differentiate between input_iterator_tag and forward_iterator_tag simply by presence of operations since you cannot reflexively check if the iterator satisfies the multipass guarantee. Additionally, you cannot really distinguish between those and output_iterator_tag if the iterator yields a mutable reference. They will have to be explicitly provided somewhere.
That leaves Option 1. Guess we should just get used to writing all the boilerplate. I, for one, welcome our new carpal-tunnel overlords.

Related

Do custom iterators always need to explicitly specify value_type?

Since iterator is deprecated, I began converting iterators in my code base to use non-deprecated constructs. I could not seem to make my indirect iterator compliant with the std::forward_iterator concept unless I explicitly specified value_type. I would like to know if this is expected.
Based on the definition of iter_value_t and indirectly_readible_traits, it seems like there is no automatic inference of std::iter_value_t. Naively, I would have expected std::iter_value_t<Itr> to be defined as std::remove_cvref_t<std::iter_reference_t<Itr>> if no definition for value_type is present (which is checked via has-member-value-type in indirectly_readible_traits).
#include <vector>
template <std::forward_iterator Itr>
class IndirectItr {
public:
using value_type = std::iter_value_t<Itr>; // **do I need this?**
explicit IndirectItr(Itr itr = {}) : m_itr{itr} {}
bool operator==(const IndirectItr& rhs) const { return m_itr == rhs.m_itr; }
bool operator!=(const IndirectItr& rhs) const { return m_itr != rhs.m_itr; }
typename std::iter_reference_t<Itr> operator *() const { return *m_itr; }
IndirectItr& operator++() { ++m_itr; return *this; }
IndirectItr operator++(int) { auto ret = *this; ++(*this); return ret; }
typename std::iter_difference_t<Itr> operator-(const IndirectItr& rhs) const { return m_itr - rhs.m_itr; }
private:
Itr m_itr;
};
using Base = std::vector<int>::iterator;
static_assert(std::forward_iterator<IndirectItr<Base>>);
static_assert(std::same_as<std::iter_value_t<Base>, std::remove_cvref_t<std::iter_reference_t<Base>>>);
P.S. I have several indirect iterator definitions that wrap other iterators. The example above is representative of a custom indirect iterator. I don't have this exact class in my code.
You don't have to have a member value_type on your iterator. But your only alternative is to specialize iterator_traits<T> for your iterator type and provide a value_type alias there. So you may as well make it a member of the iterator.
The value_type cannot be computed from something else, as it may have no obvious relation to reference or any other operation on the iterator. This is one of the things that allows for proxy iterators, which pre-C++20 concepts did not.
std::forward_iterator includes std::input_iterator, which includes std::indirectly_readable, which contains:
requires(const In in) {
typename std::iter_value_t<In>;
typename std::iter_reference_t<In>;
typename std::iter_rvalue_reference_t<In>;
{ *in } -> std::same_as<std::iter_reference_t<In>>;
{ ranges::iter_move(in) } -> std::same_as<std::iter_rvalue_reference_t<In>>;
}
(where In is std::remove_cvref_t<IndirectItr<Base>>).
That typename std::iter_value_t<In>; line requires you to declare a value_type or to specialize std::iterator_traits<IndirectItr<Base>> (and provide value_type there), as explained here.
You cannot specialize std::iterator_traits<IndirectItr<T>> for all T (see also), so you can either pick the first and very reasonable option, or fully specialize for each IndirectItr you intend to use.

Bidirectional iterator implementation

By some reasons I need to implement a bidirectional iterator, after some time, I got this result (add parameter tells what the side the iterator should move to (to avoid code duplication, when implementing reverse_iterator)):
#include <iterator>
namespace gph {
template <typename T, int add> class BidirectionalIterator;
template<typename T, int add>
void swap(BidirectionalIterator<T, add>& it1, BidirectionalIterator<T, add>& it2) {
it1.swap(it2);
}
template<typename T, int add>
class BidirectionalIterator {
private:
T *currentPosition, *begin, *end;
public:
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
using iterator_category = std::bidirectional_iterator_tag;
inline BidirectionalIterator(T* currentPosition, T* begin, T* end):currentPosition(currentPosition), begin(begin), end(end) {}
//copy constructor
inline BidirectionalIterator(const BidirectionalIterator& iterator)
:BidirectionalIterator(iterator.currentPosition, iterator.begin, iterator.end) {}
//move constructor
inline BidirectionalIterator(BidirectionalIterator&& iterator) noexcept
:BidirectionalIterator(iterator.currentPosition, iterator.begin, iterator.end) {}
//copy and move assignment statement
inline BidirectionalIterator& operator=(BidirectionalIterator iterator) {
gph::swap(*this, iterator);
}
inline void swap(BidirectionalIterator& iterator) {
std::swap(currentPosition, iterator.currentPosition);
std::swap(begin, iterator.begin);
std::swap(end, iterator.end);
}
inline reference operator*() const {
return *currentPosition; //dangerous if the iterator is in not-dereferenceable state
}
inline BidirectionalIterator& operator++() {
if (currentPosition != end) currentPosition += add;
return *this;
}
inline bool operator==(const BidirectionalIterator& iterator) const {
return currentPosition == iterator.currentPosition;
}
inline bool operator!=(const BidirectionalIterator& iterator) const {
return !(*this == iterator);
}
inline BidirectionalIterator operator++(int) {
BidirectionalIterator past = *this;
++*this;
return past;
}
inline BidirectionalIterator& operator--() {
if (currentPosition != begin) currentPosition -= add;
return *this;
}
inline BidirectionalIterator operator--(int) {
BidirectionalIterator past = *this;
--*this;
return past;
}
};
}
I've tried to satisfy MoveAssignable, MoveConstructible, CopyAssignable, CopyConstructible, Swappable, EqualityComparable, LegacyIterator, LegacyInputIterator, LegacyForwardIterator, LegacyBidirectionalIterator named requirements.
Some of their requirements are expressed in operators overloading, but some ones from them I do not know how to implement (perhaps, they are automatically implemented by other ones?), for instance: i->m or *i++ (from here). First question: how should I implement them?
Second question: is my iterator implementation good? What are its drawbacks, where did I make mistakes?
P.S. The questions are on edge of unconstructiveness, but I really need help with it. Sorry for my english.
I find it hard to find a definitive answer to this, so just some thoughts, which may be uncomplete and are open to discussion.
i->m can be implemented by inline pointer operator->() { return this->currentPosition; }
*i++ should already be covered by your implementation
I don't see any reason to swap all the pointers in operator=. For three reasons:
You are swapping the values with a local variable
The move constructor doesn't swap any values (would be inconsistent behaviour between BidirectionalIterator newIt=oldIt; and BidirectionalIterator newIt(oldIt);, but it actually isn't because of the previous point)
Those pointers are not unique resources, so there is no problem in copying them and sharing them between multiple instances
operator= is missing a return.
You have using difference_type = std::ptrdiff_t; but don't implement operator- which would return difference_type, why not implement it?
Reverse iterators can be implemented easier by std::reverse_iterator which will just wrap your iterator and invert ++ and -- and so on.
You probably want to find an easy way to implement a const version of the iterator (a version that always returns a const T& or const T*). I saw three versions of this:
duplicating all the code
using const cast
using an additional template parameter bool TIsConst and using pointer = std::conditional_t<TIsConst, const T*, T*>;
using a templated iterator with parameter const T on the other hand might appear easy, but fails to satisfy a requirement, see this question

Converting iterators and const_iterators

General context:
I am trying to build a container that will behave as as wrapper around a multi-dimensional array of run time defined dimensions - in fact the underlying array is of course a 1D array of the total size. The main part is that operator [] returns a wrapper on the sub array.
As containers need iterators, I am currently implementing iterators on that container, both Container::iterator and Container::const_iterator. I try hard to mimic standard container iterators, and they should respect all the requirements for random access and output iterators.
I have already noted the following requirements:
a public default constructor
(of course copy and move semantics)
implicit conversion from an iterator to a const_iterator
iterator and const_interator should be comparable
Specific context:
Standard containers iterators provide no conversion at all from a const_iterator to an iterator, because removing constness can be dangerous. I have already searched SO for that problem and found How to remove constness of const_iterator? where answers propose differents tricks to remove constness from an operator. So I now wonder whether I should implement an explicit conversion from a const_iterator to an iterator ala const_cast on pointers.
Question:
What are the risks in implementing an explicit conversion from a const_iterator to a (non const) iterator and how is it different from the solutions from the linked question (copied here for easier reading):
using advance and distance (constant time form my random access iterators)
iter i(d.begin());
advance (i,distance<ConstIter>(i,ci));
using erase:
template <typename Container, typename ConstIterator>
typename Container::iterator remove_constness(Container& c, ConstIterator it)
{
return c.erase(it, it);
}
For references, here is a simplified and partial implementation of my iterators:
// Base for both iterator and const_iterator to ease comparisons
template <class T>
class BaseIterator {
protected:
T *elt; // high simplification here...
BaseIterator(T* elt): elt(elt) {}
virtual ~BaseIterator() {}
public:
bool operator == (const BaseIterator& other) {
return elt == other.elt;
}
bool operator != (const BaseIterator& other) {
return ! operator == (other);
}
// other comparisons omitted...
BaseIterator& add(int n) {
elt += n;
return *this;
}
};
// Iterators<T> in non const iterator, Iterator<T, 1> is const_iterator
template <class T, int cnst=0, class U= typename std::conditional<cnst, const T, T>::type >
class Iterator: public BaseIterator<T> {
using BaseIterator<T>::elt;
public:
using value_type = U;
using reference = U*;
using pointer = U&;
using difference_type = int;
using iterator_category = std::random_access_iterator_tag;
Iterator(): BaseIterator<T>(nullptr);
Iterator(T* elt): BaseIterator<T>(elt) {}
// conversion from iterator to const_iterator
template <class X, typename = typename std::enable_if<
(cnst == 1) && std::is_same<X, T>::value>::type>
Iterator(const BaseIterator<X>& other): BaseIterator<X>(other) {};
// HERE: explicit conversion from const_iterator to non const
template <class X, typename = typename std::enable_if<
std::is_same<X, T>::value && (cnst == 0)>::type>
explicit Iterator(const Iterator<X, 1 - cnst>& other): BaseIterator<T>(other) {}
// partial implementation below
U& operator *() {
return *elt;
}
U* operator ->() {
return elt;
}
Iterator<T, cnst, U>& operator ++() {
this->add(1);
return *this;
}
};
Both the methods you quote require non-const access to the container, so you can't get access to const underlying elements as non-const.
What you are suggesting doesn't, so it can be UB [dcl.type.cv]

Create const iterator and non const iterator

I am writing an iterator class (say MyIterator) for a library.
Is it a good idea to use const overloading to make
const MyIterator acts like a const_iterator of std::vector while MyIterator acts as like iterator of std::vector ?
Will the library user/developer get confused?
The implementation would be like:
// std::iterator example
#include <iostream> // std::cout
#include <iterator> // std::iterator, std::input_iterator_tag
class MyIterator : public std::iterator<std::input_iterator_tag, int>
{
mutable int* p;
public:
MyIterator(int* x) :p(x) {}
MyIterator(const MyIterator& mit) : p(mit.p) {}
MyIterator& operator++() {++p;return *this;}
MyIterator operator++(int) {MyIterator tmp(*this); operator++(); return tmp;}
bool operator==(const MyIterator& rhs) {return p==rhs.p;}
bool operator!=(const MyIterator& rhs) {return p!=rhs.p;}
const int& operator*() const {return *p;} // <-- const overload
int& operator*() {return *p;}
};
An alternative would be using templates to implement a single iterator class that can be specialized into the const and non-const iterators. I am currently doing that (i heard boost is doing that...). But, the templates get complex very quickly as I implement range, and then range of range (as in nested range based for loop).
Using const MyIterator as a substitute of const_MyIterator (const_iterator) won't work, because const_iterator is not meant to be a constant iterator, but an iterator iterating over constant elements.
Also, with a const MyIterator, you couldn't use modifying operators, like ++ or --, because these are non-const methods, modifying the iterator itself.
So, if you want to provide some sort of const_iterator, you won't get around implementing one.
Will the library user/developer get confused?
Finally, to answer your question: Yes, I think so, because of the different behaviour (and expectations) of a const iterator vs const_iterator.

C++ custom collection reverse_iterator with similar behaviour to std::vector implementation

I have a template based custom collection (as we cannot use std::vector on the interface). I would like to implement a reverse_iterator specific to this collection. The reverse iterator struct below is a structure nested within the collection class. An iterator (basically a pointer to element type of the collection) is already implemented. This is my first attempt at a reverse iterator.
template <typename T>
struct reverse_iterator
{
typedef T::iterator iterator;
typedef T& reference;
inline reverse_iterator(const iterator & it):_it(it){}
inline reverse_iterator() : _it(0x0) {}
inline iterator base() const {iterator it = _it; return --it;}
inline reverse_iterator operator ++ () {return reverse_iterator(--_it);}
inline reverse_iterator operator -- () {return reverse_iterator(++_it);}
inline reverse_iterator operator ++ (int val) {_it -= val; return reverse_iterator(_it);}
inline reverse_iterator operator -- (int val) {_it += val; return reverse_iterator(_it);}
inline reverse_iterator operator += (int val) {_it -= val; return reverse_iterator(_it);}
inline reverse_iterator operator -= (int val) {_it += val; return reverse_iterator(_it);}
inline reverse_iterator operator + (int val) const {iterator it = _it - val; return reverse_iterator(it);}
inline reverse_iterator operator - (int val) const {iterator it = _it + val; return reverse_iterator(it);}
bool operator == (const iterator & other) const {return other == base();}
bool operator != (const iterator & other) const {return other != base();}
reference operator*() const {return *base();}
iterator operator->() const {return base();}
private:
iterator _it;
};
Is this is workable reverse_iterator or am I missing something ?
Can this be improved?
Except for the things mentioned below your implementation is almost the same as the implementation in libstdc++ (v3, but still somewhat accurate). Note that you're currently missing all non-member functions. All in all you should try to match the std::reverse_iterator interface: if you're ever able to use std types you can happily exchange your mylab::reverse_iterator by std::reverse_iterator.
Missing things
You're missing all comparison operators between reverse_iterator, such as operator==, operator!=, operator< and so on.
Strange things
This is basically a list of stuff where your reverse_iterator differs from the standard one.
Usually the pre-increment/-decrement operators return a reference (*this) and not a new object.
The post increment/decrement operators shouldn't take a value:
inline reverse_iterator operator ++ (int) {
reverse_iterator tmp = *this;
++*this; // implement post-increment in terms of pre-increment!
// or --_it;
return tmp;
}
inline reverse_iterator operator -- (int) { ... }
The compound assignment operators also usually return references.
Your const iterator& constructor should be explicit, otherwise one could accidentally mix reverse and normal iterators.
Instead of a container type T you should use the underlying iterator as template parameter:
template <typename Iterator>
struct reverse_iterator
{
typedef Iterator iterator;
typedef typename iterator_traits<Iterator>::reference reference;
...
}
This enables you to use reverse_iterator on anything that iterator_traits can handle:
template <class Iterator>
struct iterator_traits{
typedef typename Iterator::reference reference;
// Add other things
};
template <class T>
struct iterator_traits<T*>{
typedef T & reference;
};
With this you can even use reverse_iterator<int *> or similar.
operator-> usually returns a pointer to the underlying object, not an intermediary iterator. You might want to add a pointer typedef to both your traits and your original iterator.
It's very uncommon to check equality between different types. Remove the operator==(const iterator&).