How to remove constness of const_iterator?
As an extension to this question Are const_iterators
faster?, I have another question on const_iterators
. How to remove constness of a const_iterator
?
Though iterators are generalised form of pointers but still const_iterator
and iterator
s are two different things. Hence, I believe, I also cannot use const_cast<>
to covert from const_iterator
to iterator
s.
One approach could be that you define an iterator which moves 'til the element to which const_iterator
points. But this looks to be a linear time algorithm.
Any idea on what is the best way to achieve this?
Solution 1:
There is a solution with constant time complexity in C++11: for any sequence, associative, or unordered associative container (including all of the Standard Library containers), you can call the range-erase member function with an empty range:
template <typename Container, typename ConstIterator>
typename Container::iterator remove_constness(Container& c, ConstIterator it)
{
return c.erase(it, it);
}
The range-erase member functions have a pair of const_iterator
parameters, but they return an iterator
. Because an empty range is provided, the call to erase does not change the contents of the container.
Hat tip to Howard Hinnant and Jon Kalb for this trick.
Solution 2:
Unfortunately linear time is the only way to do it:
iter i(d.begin());
advance (i,distance<ConstIter>(i,ci));
where iter and constIter are suitable typedefs and d is the container over which you are iterating.
Solution 3:
In the answers to your previous post, there were a couple of people, me included, that recommended using const_iterators instead for non-performance related reasons. Readability, traceability from the design board to the code... Using const_iterators to provide mutating access to a non-const element is much worse than never using const_iterators at all. You are converting your code into something that only you will understand, with a worse design and a real maintainability pain. Using const just to cast it away is much worse than not using const at all.
If you are sure you want it, the good/bad part of C++ is that you can always get enough rope to hang yourself. If your intention is using const_iterator for performance issues, you should really rethink it, but if you still want to shoot your foot off... well C++ can provide your weapon of choice.
First, the simplest: if your operations take the arguments as const (even if internally apply const_cast) I believe it should work directly in most implementations (even if it is probably undefined behavior).
If you cannot change the functors, then you could tackle the problem from either side: provide a non-const iterator wrapper around the const iterators, or else provide a const functor wrapper around the non-const functors.
Iterator façade, the long road:
template <typename T>
struct remove_const
{
typedef T type;
};
template <typename T>
struct remove_const<const T>
{
typedef T type;
};
template <typename T>
class unconst_iterator_type
{
public:
typedef std::forward_iterator_tag iterator_category;
typedef typename remove_const<
typename std::iterator_traits<T>::value_type
>::type value_type;
typedef value_type* pointer;
typedef value_type& reference;
unconst_iterator_type( T it )
: it_( it ) {} // allow implicit conversions
unconst_iterator_type& operator++() {
++it_;
return *this;
}
value_type& operator*() {
return const_cast<value_type&>( *it_ );
}
pointer operator->() {
return const_cast<pointer>( &(*it_) );
}
friend bool operator==( unconst_iterator_type<T> const & lhs,
unconst_iterator_type<T> const & rhs )
{
return lhs.it_ == rhs.it_;
}
friend bool operator!=( unconst_iterator_type<T> const & lhs,
unconst_iterator_type<T> const & rhs )
{
return !( lhs == rhs );
}
private:
T it_; // internal (const) iterator
};
Solution 4:
Scott Meyer's article on preferring iterators over const_iterators answers this. Visage's answer is the only safe pre-C++11 alternative, but is actually constant time for well-implemented random access iterators, and linear time for others.