Iterators in C++-1
Iterators in C++-1
Iterators
1.1. Introduction
Overview
In STL, an iterator is an object that can be used to traverse through or step through the
elements in the container by using a set of operators like increment operator (++) or
important role in connecting the algorithms to the container in addition to accessing and
There are types of Iterators named: input, output, forward, bidirectional and random
access etc.
Some of the key concepts are: Iteration Protocol, Iterable, Constant & Mutable
Iterators are useful for Memory Efficiency, Modularity, and Code Reusability etc.
1.2. Definition
An iterator is used to point to the memory address of the STL container classes Apart from that,
an iterator is also used to iterate over the data structures. It can access or assign values that a
pointer is unable to do. For better understanding, you can relate them with a pointer to some
extent.
2
Iterators act as a bridge that connects algorithm to STL containers and allows the modifications
of the data present inside the container. They allow you to iterate over the container, access and
assign the values, and run different operators over them, to get the desired result. Iterators are
one of the four pillars of the standard template library or STL in C++.
They provide a uniform interface to access elements sequentially without exposing the
Depending upon the functionality of iterators they can be classified into five categories, as
shown in the diagram below with the outer one being the most powerful one and consequently
Fig 1.0
Now each one of these iterators are not supported by all the containers in STL, different containers
support different iterators, like vectors support Random-access iterators, while lists support
bidirectional iterators. The whole list is as given below:
Vector Random-Access
List Bidirectional
Deque Random-Access
Map Bidirectional
Multimap Bidirectional
Set Bidirectional
Multiset Bidirectional
1.3.1. Types of iterators: Based upon the functionality of the iterators, they can be classified
into five major categories:
1. Input Iterators: They are the weakest of all the iterators and have very limited functionality.
They can only be used in a single-pass algorithms, i.e., those algorithms which process the
container sequentially such that no element is accessed more than once. They are the iterators
that can be used in sequential input operations, where each value pointed by the iterator is read
Fig 2.0
Note* One important thing to be kept in mind is that forward, bidirectional and random access iterators are also
valid input iterators, as shown in the iterator hierarchy above.
A. Usability: Input iterators can be used only with single-pass algorithms, i.e., algorithms
in which we can go to all the locations in the range at most once, like when we have to
search or find any element in the range, we go through the locations at most once.
2. Output Iterators: Just like input iterators, they are also very limited in their functionality and
can only be used in single-pass algorithm, but not for accessing elements, but for being assigned
elements. They can be assigned values in a sequence, but cannot be used to access values, unlike
input iterators which do the reverse of accessing values and cannot be assigned values. So, we
can say that input and output iterators are complementary to each other.
5
A. Usability: Just like input iterators, Output iterators can be used only with single-pass
algorithms, i.e., algorithms in which we can go to all the locations in the range at most
once, such that these locations can be dereferenced or assigned value only once.
3. Forward Iterator: They are higher in hierarchy than input and output iterators, and contain
all the features present in these two iterators. But, as the name suggests, they also can only move
in forward direction and that too one step at a time. Forward iterators are considered to be the
combination of input as well as output iterators. It provides support to the functionality of both of
std::replace: As we know this algorithm is used to replace all the elements in the range which
are equal to a particular value by a new value. So, let us look at its internal working
6
4. Bidirectional Iterators: They have all the features of forward iterators along with the fact that
they overcome the drawback of forward iterators, as they can move in both the directions that is
std::reverse_copy: As the name suggests, this algorithm is used to copy a range into another
range, but in reverse order. Now, as far as accessing elements and assigning elements are
concerned, forward iterators are fine, but as soon as we have to decrement the iterator, then
we cannot use these forward iterators for this purpose, and that’s where bidirectional iterators
Here, we can see that we have declared last as a bidirectional iterator, as we cannot decrement a
forward iterator as done in case of last, so we cannot use it in this scenario, and we have to
declare it as a bidirectional iterator only.
5. Random-Access Iterators: They are the most powerful iterators. They are not limited to
moving sequentially, as their name suggests, they can randomly access any element inside the
container. They are the ones whose functionality is same as pointers. Random-access iterators
7
are the most complete iterators in terms of functionality. All pointer types are also valid random-
access iterators.
There are certainly quite a few ways which show that iterators are extremely useful to us and
encourage us to use it profoundly. Some of the benefits of using iterators are as listed below:
containers, as if we will not use an iterator and access elements using ‘[ ]’ operator, then we need
to be always vary of the size of the container, whereas with iterators we can simply use member
function ‘end ()’ and iterate through the contents without having to keep anything in mind.
8
9
Output:
Fig 3.0
Explanation: As can be seen in the above code that without using iterators we need to keep track
of the total elements in the container. In the beginning there were only three elements, but after
one more element was inserted into it, accordingly the for loop also had to be amended, but using
iterators, both the time the for loop remained the same. So, iterator eased our task.
2. Code reusability: Now consider if we make v a list in place of vector in the above program
and if we were not using iterators to access the elements and only using ‘[ ]’ operator, then in
that case this way of accessing was of no use for list (as they do not support random-access
iterators).
However, if we were using iterators for vectors to access the elements, then just
changing the vector to list in the declaration of the iterator would have served the purpose,
without doing anything else So, iterators support reusability of code, as they can be used to
remove elements from the container as and when we want with ease.
10
Output:
Fig 4.0
Explanation: As seen in the above code, we can easily and dynamically add and remove
elements from the container using iterator, however doing the same without using them would
have been very tedious as it would require shifting the elements every time before insertion and
after deletion.
Iterators are powerful tools that allow efficient traversal and processing of collections and
sequences in Python. By understanding the various types of iterators, from built-in and file
iterators to custom and infinite iterators, you can handle a wide range of tasks more effectively
and elegantly.
11
a strong and essential component of C++'s Standard Template Library (STL). Their application
is not without difficulties, though. Iterator compilation issues can result in a number of traps that
programmers need to avoid in order to create reliable and effective C++ applications.
Having undefined behavior when working with iterators is a big problem. When iterators are
used improperly, as when dereferencing an iterator that hasn't been initialized or that has been
rendered invalid by changes made to the container, this can happen. For instance, iterators that
are already in place may become invalid when elements are added to or removed from a
container such as ‘std::vector’. Using these invalidated iterators may lead to crashes, erratic
Example
Output
The output of this code is unpredictable and may cause a runtime crash or print a garbage value
Performance overheads from iterators can be introduced, especially in debug builds. In order to
guarantee the validity of iterators, compilers frequently include extra safety tests in debug mode,
such as range checks and iterator validity checks. These checks can greatly slow down the
program's execution, even though they are useful for finding flaws during development. Since
these tests are usually turned off in release builds to maximize performance, the performance
Example
Output:
In debug mode, this might run significantly slower due to iterator checks, but in release mode, it
2.3.Compatibility Issues
Differences in STL implementations between compilers and compiler versions might cause
compatibility problems. The behavior and interface of iterators are specified in the C++ standard,
but implementation details may vary. When using complex iterator features or transferring code
between various compiler environments, this can cause issues. For example, some extensions or
optimizations that are present in one compiler's STL could not be in another, leading to
Example
Output:
The output should be consistent, but some older or non-standard-compliant compilers may
understand, particularly in programmes with a lot of templates. Upon encountering a mistake, the
compiler frequently generates lengthy and complex warnings that may mask the true issue. This
is especially true for template instantiation failures, when the real problem could be buried in a
lengthy sequence of template expansions. To find the core reason, developers must meticulously
go back through the error messages, which can be a laborious and annoying process.
Example
16
Output:
If the iterator type is incorrect, the compiler error message can be long and complex, making it
2.5.Iterator Invalidations
understand, particularly in programmes with a lot of templates. Upon encountering a mistake, the
compiler frequently generates lengthy and complex warnings that may mask the true issue. This
is especially true for template instantiation failures, when the real problem could be buried in a
lengthy sequence of template expansions. To find the core reason, developers must meticulously
go back through the error messages, which can be a laborious and annoying process.
Example
17
Output:
The first ‘std::cout’ prints 1, but the second ‘std::cout’ results in undefined behavior due to
Iterators of a certain category are frequently needed for STL algorithms. For instance, ‘std::
find’ can function with input iterators, while ‘std::sort’ needs random access iterators. When
using an algorithm with an iterator of the wrong category, compile-time errors or erroneous
behavior may result. Correct programme operation requires knowing each algorithm's
Example
Output:
18
2.7.Concurrency Issues
Iterators in multi-threaded settings may result in unclear behavior and data races if the
iterators are not thread-safe by nature, multiple threads can modify a container at the same time,
corrupting the container's state and invalidating iterators. Iterators and the containers they are
associated with must be accessed properly in a multi-threaded context, which requires the
Example
19
Output:
Without proper locking, the output may be unpredictable, with potential data races causing
undefined behavior. With the lock, the output will be consistent and correct.
A useful tool in template metaprogramming for figuring out an iterator's characteristics is its
trait. However, serious issues with generic programming might arise from misinterpreting or
abusing iterator features. For instance, in order to guarantee accuracy or maximize performance,
algorithmic failure or subpar performance. To properly utilize iterator qualities and steer clear of
Example
20
Output
If the iterator traits are misused or misunderstood, the function might not correctly identify the
Custom containers that support iterators must comply with the STL's specified interface
requirements. This can be difficult and prone to mistakes, particularly for developers who are not
well-versed in STL conventions. Implementing different iterator actions and making sure they
follow the expected behavior of standard iterators are necessary for proper implementation.
Neglecting to accomplish this may result in inconspicuous errors and problems with STL
algorithms' compatibility.
Example
22
Output:
If the iterator traits are misused or misunderstood, the function might not correctly identify the
Iterators that point to elements in a container but prevent changes to the elements they point to
are known as constant iterators. They guarantee that the elements cannot be unintentionally or
purposely changed by the iterator, which is crucial when the integrity of the data inside the
means of constant iterators. This is helpful in situations when maintaining data integrity
Safety: Constant iterators assist prevent unintentional side effects and issues that can
Code Clarity: By expressing that the elements being accessed should not and cannot be
updated, the use of constant iterators clarifies the intent of the code.
Example
24
In this example, ‘const_iterator’ is used to iterate through the vector vec. Since the iterator is
constant, trying to modify the elements (e.g., *it = 10;) would result in a compilation error.
Output:
However, elements that they point to can be modified using mutable iterators. Read and write
operations are made possible by their complete access to the container's elements. When it comes
to traversing a container and having to make changes to its elements, mutable iterators are
employed.
Flexibility: They enable both retrieval and modification of the container pieces and offer
Example
In this example, iterator is used to iterate through the vector vec and modify each element by
doubling its value. The second loop then prints the modified elements.
26
Output
Detailed Explanation
Constant Iterators: Declared using the const_iterator type provided by the container. Use
Mutable Iterators: Declared using the iterator type provided by the container. Use ‘begin()’ and
Increment and Decrement: Both constant and mutable iterators support the increment
Dereferencing: Both types can be dereferenced using the * operator. For constant
Comparison: Both types support comparison operations (==, !=, <, >, <=, >=) to
In C++ programming, constant and mutable iterators have different functions and provide
different degrees of access to container items. Mutable iterators offer flexibility for dynamic
updates and modifications, while constant iterators priorities data integrity by limiting
modifications. Developers can utilize these iterators to safely and efficiently modify container
A fundamental component of the C++ Standard Template Library, iterators offers a strong and
adaptable way to retrieve and modify container items. They do have some possible drawbacks,
problems, mishandling of iterator traits, and the difficulty of maintaining customised containers.
It is essential to comprehend these hazards and know how to avoid them in order to write reliable
and effective C++ programmes. Leveraging the full capability of iterators while avoiding their
usual pitfalls requires careful coding methods, comprehensive testing, and proper understanding.
29
References