error: invalid operands to binary expression when comparing iterators using !=

Viewed 1436

I am trying to reverse a std::string:

void reverseString(vector<char>& s)
{
   auto i = s.begin();
   auto j = s.rbegin();

   while (i != j) 
   {
      char tmp = *i;
      *i = *j;
      *j = tmp;

      i++;
      j--;
   }
}

However, this happened when I tried to compare iterators

ERROR:

Line 6: Char 17: error: invalid operands to binary expression 
('__gnu_cxx::__normal_iterator<char *, std::vector<char, std::allocator<char> > >' and
'std::reverse_iterator<__gnu_cxx::__normal_iterator<char *, std::vector<char, std::allocator<char> > > >')
        while(i != j) {

3 Answers

You have a few issues in your code:

  • Firstly i has the type std::vector<char>::iterator and the j has the type std::vector<char>::reverse_iterator, which are not same. Therefore you can not do
    while(i != j) 
    
    That is the reason for the compiler error!
  • Secondly, a reverse iterator should be as like a normal iterator. enter image description here Meaning j--; will try to move one past which is an end iterator and dereferencing in in next iteration that invokes undefined behaviour. You should be instead, incrementing it to iterate from the last of the container.
  • Last but not least, you should only go to half of the container to reverse it. Otherwise, you will be swapping twice and will get the same vector you passed.

Following is the corrected code: See a demo

#include <iostream>
#include <iterator>
#include <algorithm> // std::copy_n, std::swap
#include <vector>

void reverseString(std::vector<char>& s)
{
   auto i = s.begin();
   auto j = s.rbegin();
   const auto half = s.size() / 2u;

   while (i != s.begin() + half || j != s.rbegin() + half)
   {
      // your saping code or simply `std::swap` the elements
      std::swap(*i, *j);
      ++i;
      ++j; // increment both iterators
   }
}

int main()
{
   std::vector<char> vec{ 'a', 'b', 'c' };
   reverseString(vec);
   // print the reversed vector
   std::copy_n(vec.cbegin(), vec.size(), std::ostream_iterator<char>(std::cout, " "));
}

Output:

c b a 

As a side note:

The error message already says it

('__gnu_cxx::__normal_iterator<char *, std::vector<char, std::allocator > >' and 'std::reverse_iterator<__gnu_cxx::__normal_iterator<char *, std::vector<char, std::allocator > > >')

When you remove the common, irrelevant stuff inside the angle brackets, you get

__gnu_cxx::__normal_iterator<...> and std::reverse_iterator<__gnu_cxx::__normal_iterator<...> >

This means you have two different types, for which no appropriate comparison operator is defined.


To solve this, you might compare with std::reverse_iterator::base(), e.g.

while (i != j.base()) {
    // ...
}

But also note

The base iterator refers to the element that is next (...) to the element the reverse_iterator is currently pointing to.

This means, you must also check one beyond and the loop condition becomes

while (i != j.base() && i != j.base() - 1) {
    // ...
}

Unrelated, but instead of doing the swap yourself manually, you might use std::iter_swap

while (i != j.base() && i != j.base() - 1) {
    std::iter_swap(i, j);
    ++i;
    ++j;
}

It seems you know that you have to dereference iterators as in *i = *j;.
You also need to do that in while(i != j), because you can compare chars, but not iterators. Especially not iterators of different kinds, as dgrandm mentions in his comment.

I.e.

while(*i != *j)

This gets the type problem fixed.
It is however likely that you are actually trying to not compare the value of the elements but instead their position in the vector. I.e. in order to reverse the whole vector.
Comparing positions is not possible with iterators
(to be precise: comparing for lesser/greater, see below on the appreciated input by Alan Birtles). Because iterators intentionally hide the differences between different containers. Vectors do have a comparable position, but other containers do not have one. Think of linked lists for example.
You would not be able to compare positions in a linked list, at least not by comparing iterators. Hence the container-abstracting iterators also hide the comparability. This in turn means that you cannot compare positions via iterators, even if the container you are currently using would allow it.

To clarify: Comparing iterators is possible (like "is this the same element?"), as Alan Birtles mentions, but only for equality. The comparision you need however is for lesser/greater (like "did these two iterators pass each other?"). This becomes apparent if you think of containes with an even number of elements. In that case with i++;j--; the two iterators never point to the same element and your loop would fail. You could equality-check before AND after changing the second iterator, if that were possible for the two DIFFERENT iterators you are using.

Related