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:
iterator
to a const_iterator
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.
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]