views:

212

answers:

4

In the printMessage if you access the vector of a constant class using the index it works fine, but not with the the iterator (*itr). If the iterator is declared as constant_iterator then it works fine.

Why?

In both cases I am reading the data and not modifying the vector. Can someone shed some light?

 #include <iostream> 
 #include <vector>
 #include <sstream>

 //Set this define to enable the block to compile.
 #define WILL_WORK 1
 #define WILL_NOT_WORK !WILL_WORK

 class TestMessage
 {
 public:
  TestMessage(){};
  typedef std::vector<int>  TestVec;
  typedef std::vector<int>::iterator TestItr;
  //The const iterator will work
  //typedef std::vector<uint32_t>::const_iterator TestItr;
  typedef std::vector<int>::size_type TestSize;
  TestVec m_testVector;
 };


 void printMessage(const TestMessage & tmessage)
 {
  std::ostringstream asciiMessage;

  asciiMessage << tmessage.m_testVector.size() << ",";

 #if WILL_NOT_WORK

 //This will not work
 // MS Visual Studio
 // error C2440: 'initializing' : cannot convert from
 // 'std::_Vector_const_iterator<_Ty,_Alloc>' to
 //     'std::_Vector_iterator<_Ty,_Alloc>'
 // GCC 
 // error: conversion from
 // '__gnu_cxx::__normal_iterator<const int*,
 //                               std::vector<int, std::allocator<int> > >'
 // to non-scalar type
 // '__gnu_cxx::__normal_iterator<int*,
 //                               std::vector<int, std::allocator<int> > >'
 // requested

  for (TestMessage::TestItr itr = tmessage.m_testVector.begin();
       itr != tmessage.m_testVector.end();
       ++itr)
  {
   asciiMessage << *itr;
  }

 #endif 

 #if WILL_WORK

  // This will work
  for(TestMessage::TestSize index = 0;
      index < tmessage.m_testVector.size();
      ++index)
  {
   asciiMessage << tmessage.m_testVector[index] << ",";
  }

 #endif

  asciiMessage << std::endl;

  std::cout << asciiMessage.str();
 }

 int main()
 {
  TestMessage message;
  message.m_testVector.push_back(10);
  message.m_testVector.push_back(20);
  message.m_testVector.push_back(30);
  message.m_testVector.push_back(40);
  printMessage(message);
  return 0;
 }
+4  A: 

There are 2 different [] operators. One const, one non-const.

The const-[] operator returns a const-reference and therefore the value at the index cannot be changed.

The normal iterator is non-const, meaning that the compiler might think that you could change the vector. After all, you could pass the iterator to a function and then there is no way that the compiler can guarantee that the called function will not change the contents of the iterator/vector.

Therefore, there is also a const_iterator. A const_iterator cannot be used to change the values in the vector. This can be checked directly by the compiler. If you pass a const_iterator to a function, the compiler can only assume that the called function behaves as it should be, namely not changing where the const_iterator is pointing to.

Patrick
A: 

An iterator (as opposed to const_iterator) on a vector of constant elements is not permitted. The iterator allows you to modify the vector element as well as read it. The compiler doesn't check to see whether you do modify it; it just prohibits using the iterator altogether. The const_iterator allows you to read const elements.

Vanessa MacDougal
A: 

Because a (non-const) iterator would allow you to modify the object, even if you don't do so. C++'s const enforcement is based purely on types -- to enforce based on what you do instead, it would have to have run-time monitoring of what you wrote to. It would take serious hardware support to do that without serious performance issues.

Jerry Coffin
A: 

You are passing your TestMessage object as a const reference to printMessage. Because of that, when you try to iterate over this object vector the compiler expects a const_iterator. Since it is not possible to convert it to a non-const iterator (it is not possible to automatically convert an int* to const int*, the underlying implementation of these iterators), the compilation will fail.

However, when you use operator[] with the vector, you get automatically a const reference to an int in the desired position, considering that this operator has an overloaded version to deal with constants.

If you change your declaration of printMessage to this void printMessage(TestMessage & tmessage), it will compile. But you should not do that, since you would break const-correcteness, as your print message function clearly has no intent on modifying the TestMessage object passed as argument.

fogo