Постоянная правильность

В printMessage, если вы обращаетесь к вектору постоянного класса, используя индекс, он работает нормально, но не с итератором (*itr). Если итератор объявлен как Constant_iterator, то он работает нормально.

Почему?

В обоих случаях я читаю данные и не изменяю вектор. Может кто-нибудь пролить свет?

 #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;
 }

person schakkere    schedule 11.02.2010    source источник


Ответы (4)


Есть 2 разных оператора []. Один константный, один неконстантный.

Оператор const-[] возвращает константную ссылку, поэтому значение в индексе не может быть изменено.

Обычный итератор не является константой, а это означает, что компилятор может подумать, что вы можете изменить вектор. В конце концов, вы можете передать итератор функции, и тогда компилятор никак не может гарантировать, что вызываемая функция не изменит содержимое итератора/вектора.

Следовательно, есть еще и const_iterator. Const_iterator нельзя использовать для изменения значений в векторе. Это может быть проверено непосредственно компилятором. Если вы передаете const_iterator функции, компилятор может только предположить, что вызываемая функция ведет себя так, как должно быть, а именно, не меняет того, на что указывает const_iterator.

person Patrick    schedule 11.02.2010

Итератор (в отличие от const_iterator) для вектора постоянных элементов не разрешен. Итератор позволяет вам изменять элемент вектора, а также читать его. Компилятор не проверяет, вносите ли изменения вы; он просто полностью запрещает использование итератора. const_iterator позволяет вам читать константные элементы.

person Vanessa MacDougal    schedule 11.02.2010

Потому что (неконстантный) итератор позволит изменить объект, даже если вы этого не сделаете. Принудительное применение const в С++ основано исключительно на типах — для принудительного применения на основе того, что вы делаете вместо этого, он должен иметь мониторинг во время выполнения того, что вы написали. Для этого потребуется серьезная аппаратная поддержка без серьезных проблем с производительностью.

person Jerry Coffin    schedule 11.02.2010

Вы передаете свой объект TestMessage как константную ссылку на printMessage. Из-за этого, когда вы пытаетесь выполнить итерацию по этому вектору объекта, компилятор ожидает const_iterator. Поскольку невозможно преобразовать его в неконстантный итератор (невозможно автоматически преобразовать int* в const int*, базовую реализацию этих итераторов), компиляция завершится ошибкой.

Однако, когда вы используете operator[] с вектором, вы автоматически получаете константную ссылку на int в желаемой позиции, учитывая, что у этого оператора есть перегруженная версия для работы с константами.

Если вы измените объявление printMessage на это void printMessage(TestMessage & tmessage), оно скомпилируется. Но вы не должны не этого делать, так как вы нарушите константную корректность, так как ваша функция вывода сообщения явно не намерена изменять объект TestMessage, переданный в качестве аргумента.

person fogo    schedule 11.02.2010