Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between const_iterator and non-const iterator in the C++ STL?

People also ask

What is the difference between iterator and const_iterator?

There is no performance difference. A const_iterator is an iterator that points to const value (like a const T* pointer); dereferencing it returns a reference to a constant value ( const T& ) and prevents modification of the referenced value: it enforces const -correctness.

What is a const iterator?

A const iterator points to an element of constant type which means the element which is being pointed to by a const_iterator can't be modified. Though we can still update the iterator (i.e., the iterator can be incremented or decremented but the element it points to can not be changed).

What are the STL iterators and what is their purpose?

Iterators are used to point at the memory addresses of STL containers. They are primarily used in sequences of numbers, characters etc. They reduce the complexity and execution time of the program.


const_iterators don't allow you to change the values that they point to, regular iterators do.

As with all things in C++, always prefer const, unless there's a good reason to use regular iterators (i.e. you want to use the fact that they're not const to change the pointed-to value).


They should pretty much be self-explanatory. If iterator points to an element of type T, then const_iterator points to an element of type 'const T'.

It's basically equivalent to the pointer types:

T* // A non-const iterator to a non-const element. Corresponds to std::vector<T>::iterator
T* const // A const iterator to a non-const element. Corresponds to const std::vector<T>::iterator
const T* // A non-const iterator to a const element. Corresponds to std::vector<T>::const_iterator

A const iterator always points to the same element, so the iterator itself is const. But the element it points to does not have to be const, so the element it points to can be changed. A const_iterator is an iterator that points to a const element, so while the iterator itself can be updated (incremented or decremented, for example), the element it points to can not be changed.


Unfortunaty, a lot of the methods for the STL containers takes iterators instead of const_iterators as parameters. So if you have a const_iterator, you can't say "insert an element before the element that this iterator points to" (saying such a thing is not conceptually a const violation, in my opinion). If you want do that anyway, you have to convert it to a non-const iterator using std::advance() or boost::next(). Eg. boost::next(container.begin(), std::distance(container.begin(), the_const_iterator_we_want_to_unconst)). If container is a std::list, then the running time for that call will be O(n).

So the universal rule to add const wherever it is "logical" to do so, is less universal when it comes to STL containers.

However, boost containers take const_iterators (eg. boost::unordered_map::erase()). So when you use boost containers you can be "const agressive". By the way, do anyone know if or when the STL containers will be fixed?


Use const_iterator whenever you can, use iterator when you have no other choice.


Minimal runnable examples

Non-const iterators allow you to modify what they point to:

std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
*it = 1;
assert(v[0] == 1);

Const iterators don't:

const std::vector<int> v{0};
std::vector<int>::const_iterator cit = v.begin();
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;

As shown above, v.begin() is const overloaded, and returns either iterator or const_iterator depending on the const-ness of the container variable:

  • How does begin() know which return type to return (const or non-const)?
  • how does overloading of const and non-const functions work?

A common case where const_iterator pops up is when this is used inside a const method:

class C {
    public:
        std::vector<int> v;
        void f() const {
            std::vector<int>::const_iterator it = this->v.begin();
        }
        void g(std::vector<int>::const_iterator& it) {}
};

const makes this const, which makes this->v const.

You can usually forget about it with auto, but if you starting passing those iterators around, you will need to think about them for the method signatures.

Much like const and non-const, you can convert easily from non-const to const, but not the other way around:

std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();

// non-const to const.
std::vector<int>::const_iterator cit = it;

// Compile time error: cannot modify container with const_iterator.
//*cit = 1;

// Compile time error: no conversion from const to no-const.
//it = ci1;

Which one to use: analogous to const int vs int: prefer const iterators whenever you can use them (when you don't need to modify the container with them), to better document your intention of reading without modifying.