Open In App

Bidirectional Iterators in C++

Last Updated : 08 Apr, 2024
Summarize
Comments
Improve
Suggest changes
Like Article
Like
Save
Share
Report
News Follow

After going through the template definition of various STL algorithms like std::reverse, std::next_permutation and std::reverse_copy you must have found their template definition consisting of objects of type Bidirectional Iterator. So what are they and why are they used ?

Bidirectional iterators are one of the five main types of iterators present in C++ Standard Library, others being Input iterators, Output iterator, Forward iterator and Random – access iterators.

Bidirectional iterators are iterators that can be used to access the sequence of elements in a range in both directions (towards the end and towards the beginning). They are similar to forward iterators, except that they can move in the backward direction also, unlike the forward iterators, which can move only in the forward direction.

It is to be noted that containers like list, map, multimap, set and multiset support bidirectional iterators. This means that if we declare normal iterators for them, and then those will be bidirectional iterators, just like in case of vectors and deque they are random-access iterators.

One important thing to be kept in mind is that random-access iterators are also valid bidirectional iterators, as shown in the iterator hierarchy above.

Salient Features

  1. Usability: Since, forward iterators can be used in multi-pass algorithms, i.e., algorithm which involves processing the container several times in various passes, therefore bidirectional iterators can also be used in multi-pass algorithms.
    .
  2. Equality / Inequality Comparison: A Bidirectional iterator can be compared for equality with another iterator. Since, iterators point to some location, so the two iterators will be equal only when they point to the same position, otherwise not.

    So, the following two expressions are valid if A and B are Bidirectional iterators:

    A == B  // Checking for equality
    A != B  // Checking for inequality
    
  3. Dereferencing: Because an input iterator can be dereferenced, using operator * and -> as an rvalue and an output iterator can be dereferenced as an lvalue, so forward iterators being the combination of both can be used for both the purposes, and similarly, bidirectional operators can also serve both the purposes.




    // C++ program to demonstrate bidirectional iterator
    #include<iostream>
    #include<list>
    using namespace std;
    int main()
    {
        list<int>v1 = {1, 2, 3, 4, 5};
      
        // Declaring an iterator
        list<int>::iterator i1;
      
        for (i1=v1.begin();i1!=v1.end();++i1)
        {
            // Assigning values to locations pointed by iterator
            *i1 = 1;
        }
      
        for (i1=v1.begin();i1!=v1.end();++i1)
        {
            // Accessing values at locations pointed by iterator
            cout << (*i1) << " ";
        }
          
        return 0;
    }

    
    

    Output:

    1 1 1 1 1
    

    So, as we can see here we can both access as well as assign value to the iterator, therefore the iterator is a bidirectional iterator.

  4. Incrementable: A Bidirectional iterator can be incremented, so that it refers to the next element in sequence, using operator ++().

    So, the following two expressions are valid if A is a bidirectional iterator:

    A++   // Using post increment operator
    ++A   // Using pre increment operator
    
  5. Decrementable: This is the feature which differentiates a Bidirectional iterator from a forward iterator. Just like we can use operator ++() with bidirectional iterators for incrementing them, we can also decrement them.

    That is why, its name is bidirectional, which shows that it can move in both directions.




    // C++ program to demonstrate bidirectional iterator
    #include<iostream>
    #include<list>
    using namespace std;
    int main()
    {
        list<int>v1 = {1, 2, 3, 4, 5};
      
        // Declaring an iterator
        list<int>::iterator i1;
      
        // Accessing the elements from end using decrement
        // operator
        for (i1=v1.end();i1!=v1.begin();--i1)
        {
            if (i1 != v1.end())
            {
                cout << (*i1) << " ";
            }
        }
        cout << (*i1);
          
        return 0;
    }

    
    

    Output:

    5 4 3 2 1
    

    Since, we are starting from the end of the list and then moving towards the beginning by decrementing the pointer, which shows that decrement operator can be used with such iterators. Here, we have run the loop till the iterator becomes equal to the begin(), that is why the first value is not printed inside the loop and we have printed it separately.

  6. Swappable: The value pointed to by these iterators can be exchanged or swapped.

Practical implementation

After understanding its features, it is very important to learn about its practical implementation as well. As told earlier, Bidirectional iterators can be used for all the purposes that forward iterator can be used along within those situations where iterator needs to be decremented. The following two STL algorithms can show this fact:

  • 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 come for our rescue.




    // Definition of std::reverse_copy()
    template 
    OutputIterator reverse_copy(BidirectionalIterator first,
                                BidirectionalIterator last,
                                OutputIterator result) 
    {
        while (first != last) 
        *result++ = *--last;
        return result;
    }

    
    

    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.

  • std::random_shuffle: As we know this algorithm is used to randomly shuffle all the elements present in a container. So, let us look at its internal working (Don’t go into detail just look where bidirectional iterators can be used and where they cannot be):




    // Definition of std::random_shuffle()
    template 
    void random_shuffle(RandomAccessIterator first,
                        RandomAccessIterator last,
                        RandomNumberGenerator& gen)
    {
        iterator_traits::difference_type i, n;
        n = (last - first);
        for (i=n-1; i>0; --i) 
        {
            swap (first[i],first[gen(i+1)]);
        }
    }

    
    

    Here, we can see that we have made use of Random-access iterators, as although we can increment a bidirectional iterator, decrement it as well, but we cannot apply arithmetic operator like +, – to it and this what is required in this algorithm , where (last – first) is done, so, for this reason, we cannot use a bidirectional iterator in these scenarios.

Note: As we know that Bidirectional iterator is higher in the hierarchy than forward iterator which is itself higher than input and output iterators, therefore, all these three types of iterators can be substituted by bidirectional iterators, without affecting the working of the algorithm.

So, the two above examples very well show when, where, why and how bidirectional iterators are used practically.

Limitations

After studying the salient features, one must also know its deficiencies as well although there are not as many as there are in input or output iterators as it is higher in the hierarchy.

  1. Relational Operators: Although, Bidirectional iterators can be used with equality operator (==), but it can not be used with other relational operators like , =.
    If A and B are Bidirectional iterators, then
    
    A == B     // Allowed
    A <= B     // Not Allowed
    
  2. Arithmetic Operators: Similar to relational operators, they also can’t be used with arithmetic operators like +, – and so on. This means that bidirectional iterators can move in both the direction, but sequentially.
    If A and B are Bidirectional iterators, then
    
    A + 1     // Not allowed
    B - 2     // Not allowed
    




    // C++ program to demonstrate bidirectional iterator
    #include<iostream>
    #include<list>
    using namespace std;
    int main()
    {
        list<int>v1 = {1, 2, 3, 4, 5};
      
        // Declaring first iterator
        list<int>::iterator i1;
      
        // Declaring second iterator
        list<int>::iterator i2;
      
        // i1 points to the beginning of the list
        i1 = v1.begin();
      
        // i2 points to the end of the list
        i2 = v1.end();
      
        // Applying relational operator to them
        if ( i1 > i2)
        {
            cout << "Yes";
        
        // This will throw an error because relational
        // operators cannot be applied to bidirectional iterators
      
      
        // Applying arithmetic operator to them
        int count = i1 - i2;
        // This will also throw an error because arithmetic 
        // operators cannot be applied to bidirectional iterators  
        return 0;
    }

    
    

    Output:

    Error, because of use of arithmetic and relational operators 
    with bidirectional iterators 
    
  3. Use of offset dereference operator ([ ]): Bidirectional iterators doesnot support offset dereference operator ([ ]), which is used for random-access.
    If A is a Bidirectional iterator, then
    A[3]    // Not allowed 
    


Previous Article
Next Article

Similar Reads

Different types of range-based for loop iterators in C++
Range-Based 'for' loops have been included in the language since C++11. It automatically iterates (loops) over the iterable (container). This is very efficient when used with the standard library container (as will be used in this article) as there will be no wrong access to memory outside the scope of the iterable. The loop will automatically star
5 min read
Erase Range of Elements From List Using Iterators in C++ STL
Prerequisites:List in C++Iterators in C++ A list is a type of container which requires the same properties as a doubly linked list. We can insert elements from either side of the list, but accessing elements with an index is not possible in the list. So, removing elements from the list is not an easy task. But, we have a method to remove multiple e
2 min read
Random Access Iterators in C++
After going through the template definition of various STL algorithms like std::nth_element, std::sort, you must have found their template definition consisting of objects of type Random-access Iterator. So what are they and why are they used?Random-access iterators are one of the five main types of iterators present in C++ Standard Library, others
6 min read
Forward Iterators in C++
After going through the template definition of various STL algorithms like std::search, std::search_n, std::lower_bound, you must have found their template definition consisting of objects of type Forward Iterator. So what are they and why are they used ? Forward iterators are one of the five main types of iterators present in C++ Standard Library,
6 min read
Difference between Iterators and Pointers in C++ with Examples
In C++ programming, we have both pointers and iterators that are used in managing and manipulating data structures. There are many similarities between iterators and pointers in their ability to reference and dereference memory, but there are certain differences between the two. Understanding the difference is very important in C++ programming. Poi
4 min read
Introduction to Iterators in C++
An iterator is an object (like a pointer) that points to an element inside the container. We can use iterators to move through the contents of the container. They can be visualized as something similar to a pointer pointing to some location and we can access the content at that particular location using them. Iterators play a critical role in conne
6 min read
Why Use Iterators Instead of Array Indices?
In C++, both iterators and array indices are used to access and manipulate elements in a container, such as arrays, vectors, and lists. However, one common question that arises is why should we prefer using iterators over array indices in certain scenarios? In this article, we will learn the advantages of using iterators over array indices, and pro
3 min read
Output Iterators in C++
After going through the template definition of various STL algorithms like std::copy, std::move, std::transform, you must have found their template definition consisting of objects of type Output Iterator. So what are they and why are they used ? Output iterators are one of the five main types of iterators present in C++ Standard Library, others be
6 min read
Input Iterators in C++
After going through the template definition of various STL algorithms like std::find, std::equal, std::count, you must have found their template definition consisting of objects of type Input Iterator. So what are they and why are they used?Input iterators are one of the five main types of iterators present in the C++ Standard Library, others being
6 min read
Iterators Operations in C++
Iterators are pointer like objects that are used to refer iterator the STL containers. Just like pointer arithmetic, there are some operations that are allowed on iterators in C++. They are used to provide different functionalities to make iterator more powerful and versatile. In this article, we will learn about iterator operations (also called it
9 min read
Iterators in C++ STL
An iterator in C++ is a pointer-like object that points to an element of the STL container. They are generally used to loop through the contents of the STL container in C++. The main advantage of STL iterators is that they make the STL algorithms independent of the type of container used. We can just pass the iterator to the container elements inst
11 min read
C++ Programming Language
C++ is the most used and most popular programming language developed by Bjarne Stroustrup. C++ is a high-level and object-oriented programming language. This language allows developers to write clean and efficient code for large applications and software development, game development, and operating system programming. It is an expansion of the C pr
9 min read
30 OOPs Interview Questions and Answers (2024) Updated
Object-oriented programming, or OOPs, is a programming paradigm that implements the concept of objects in the program. It aims to provide an easier solution to real-world problems by implementing real-world entities such as inheritance, abstraction, polymorphism, etc. in programming. OOPs concept is widely used in many popular languages like Java,
15+ min read
C++ Interview Questions and Answers (2024)
C++ - the must-known and all-time favourite programming language of coders. It is still relevant as it was in the mid-80s. As a general-purpose and object-oriented programming language is extensively employed mostly every time during coding. As a result, some job roles demand individuals be fluent in C++. It is utilized by top IT companies such as
15+ min read
vector erase() and clear() in C++
The std::vector::erase() and std::vector::clear() methods in C++ are used to remove elements from the std::vector container. They are the member function of std::vector class defined inside <vector> header file. In this article, we will learn how to use vector::erase() and vector::clear() in C++. Table of Content vector::erase()vector::clear(
3 min read
Virtual Function in C++
A virtual function (also known as virtual methods) is a member function that is declared within a base class and is re-defined (overridden) by a derived class. When you refer to a derived class object using a pointer or a reference to the base class, you can call a virtual function for that object and execute the derived class's version of the meth
6 min read
Inheritance in C++
The capability of a class to derive properties and characteristics from another class is called Inheritance. Inheritance is one of the most important features of Object Oriented Programming in C++. In this article, we will learn about inheritance in C++, its modes and types along with the information about how it affects different properties of the
15+ min read
C++ Classes and Objects
In C++, classes and objects are the basic building block that leads to Object-Oriented programming in C++. In this article, we will learn about C++ classes, objects, look at how they work and how to implement them in our C++ program. What is a Class in C++?A class is a user-defined data type, which holds its own data members and member functions, w
10 min read
Decision Making in C (if , if..else, Nested if, if-else-if )
The conditional statements (also known as decision control structures) such as if, if else, switch, etc. are used for decision-making purposes in C programs. They are also known as Decision-Making Statements and are used to evaluate one or more conditions and make the decision whether to execute a set of statements or not. These decision-making sta
11 min read
Convert String to int in C++
Converting a string to int is one of the most frequently encountered tasks in C++. As both string and int are not in the same object hierarchy, we cannot perform implicit or explicit type casting as we can do in case of double to int or float to int conversion. Conversion is mostly done so that we can convert numbers that are stored as strings. Exa
8 min read
Priority Queue in C++ Standard Template Library (STL)
A C++ priority queue is a type of container adapter, specifically designed such that the first element of the queue is either the greatest or the smallest of all elements in the queue, and elements are in non-increasing or non-decreasing order (hence we can see that each element of the queue has a priority {fixed order}). In C++ STL, the top elemen
11 min read
Vector in C++ STL
Vectors are the same as dynamic arrays with the ability to resize themselves automatically when an element is inserted or deleted, with their storage being handled automatically by the container. Vector elements are placed in contiguous storage so that they can be accessed and traversed using iterators. In vectors, data is inserted at the end. Inse
11 min read
Set in C++ Standard Template Library (STL)
Sets are a type of associative container in which each element has to be unique because the value of the element identifies it. The values are stored in a specific sorted order i.e. either ascending or descending. The std::set class is the part of C++ Standard Template Library (STL) and it is defined inside the <set> header file. Syntax: std:
7 min read
Map in C++ Standard Template Library (STL)
Maps are associative containers that store elements in a mapped fashion. Each element has a key value and a mapped value. No two mapped values can have the same key values. std::map is the class template for map containers and it is defined inside the <map> header file. Basic std::map Member FunctionsSome basic functions associated with std::
9 min read
C++ Standard Template Library (STL)
The C++ Standard Template Library (STL) is a set of template classes and functions that provides the implementation of common data structures and algorithms such as lists, stacks, arrays, sorting, searching, etc. It also provides the iterators and functors which makes it easier to work with algorithms and containers. STL was originally designed by
9 min read
Object Oriented Programming in C++
Object-oriented programming - As the name suggests uses objects in programming. Object-oriented programming aims to implement real-world entities like inheritance, hiding, polymorphism, etc. in programming. The main aim of OOP is to bind together the data and the functions that operate on them so that no other part of the code can access this data
10 min read
Operator Overloading in C++
in C++, Operator overloading is a compile-time polymorphism. It is an idea of giving special meaning to an existing operator in C++ without changing its original meaning. In this article, we will further discuss about operator overloading in C++ with examples and see which operators we can or cannot overload in C++. C++ Operator OverloadingC++ has
9 min read
Templates in C++ with Examples
A template is a simple yet very powerful tool in C++. The simple idea is to pass the data type as a parameter so that we don't need to write the same code for different data types. For example, a software company may need to sort() for different data types. Rather than writing and maintaining multiple codes, we can write one sort() and pass the dat
10 min read
Friend Class and Function in C++
A friend class can access private and protected members of other classes in which it is declared as a friend. It is sometimes useful to allow a particular class to access private and protected members of other classes. For example, a LinkedList class may be allowed to access private members of Node. We can declare a friend class in C++ by using the
6 min read
Bitwise Operators in C
In C, the following 6 operators are bitwise operators (also known as bit operators as they work at the bit-level). They are used to perform bitwise operations in C. The & (bitwise AND) in C takes two numbers as operands and does AND on every bit of two numbers. The result of AND is 1 only if both bits are 1. The | (bitwise OR) in C takes two nu
7 min read
Article Tags :
Practice Tags :
three90RightbarBannerImg