Константные ссылки при разыменовании итератора в наборе, начиная с Visual Studio 2010.

Начиная с Visual Studio 2010, итерация по набору, по-видимому, возвращает итератор, который разыменовывает данные как «константные данные», а не как неконстантные.

Следующий код является примером того, что компилируется в Visual Studio 2005, но не в 2010 (это искусственный пример, но он ясно иллюстрирует проблему, которую мы обнаружили в нашем собственном коде).

В этом примере у меня есть класс, который хранит позицию вместе с температурой. Я определяю операторы сравнения (не все, достаточно для иллюстрации проблемы), которые используют только положение, а не температуру. Дело в том, что для меня два экземпляра идентичны, если позиция идентична; Меня не волнует температура.

#include <set>

class DataPoint
   {
   public:
      DataPoint (int x, int y) : m_x(x), m_y(y), m_temperature(0) {}
      void setTemperature(double t) {m_temperature = t;}
      bool operator<(const DataPoint& rhs) const
         {
         if (m_x==rhs.m_x) return m_y<rhs.m_y;
         else              return m_x<rhs.m_x;
         }
      bool operator==(const DataPoint& rhs) const
         {
         if (m_x!=rhs.m_x) return false;
         if (m_y!=rhs.m_y) return false;
         return true;
         }
   private:
      int m_x;
      int m_y;
      double m_temperature;
   };

typedef std::set<DataPoint> DataPointCollection;

void main(void)
{
DataPointCollection points;

points.insert (DataPoint(1,1));
points.insert (DataPoint(1,1));
points.insert (DataPoint(1,2));
points.insert (DataPoint(1,3));
points.insert (DataPoint(1,1));

for (DataPointCollection::iterator it=points.begin();it!=points.end();++it)
   {
   DataPoint &point = *it;
   point.setTemperature(10);
   }
}

В основной подпрограмме у меня есть набор, к которому я добавляю несколько точек. Чтобы проверить правильность оператора сравнения, я добавляю точки данных с одной и той же позицией несколько раз. При написании содержимого набора я четко вижу, что в наборе всего 3 точки.

Цикл for проходит по набору и устанавливает температуру. Логически это разрешено, так как температура не используется в операторах сравнения.

Этот код корректно компилируется в Visual Studio 2005, но выдает ошибки компиляции в Visual Studio 2010 в следующей строке (в цикле for):

   DataPoint &point = *it;

Указанная ошибка заключается в том, что он не может назначить «const DataPoint» [неконстантному] «DataPoint &».

Кажется, что у вас нет приличного (= не грязного) способа написания этого кода в VS2010, если у вас есть оператор сравнения, который сравнивает только части элементов данных.

Возможные решения:

  • Добавление const-cast в строку, где выдает ошибку
  • Сделать температуру изменяемой и сделать setTemperature константным методом

Но мне оба решения кажутся довольно «грязными».

Похоже, комитет по стандартам C++ проглядел эту ситуацию. Или нет?

Каковы чистые решения для решения этой проблемы? Кто-нибудь из вас сталкивался с такой же проблемой и как вы ее решили?

Патрик


person Patrick    schedule 26.03.2010    source источник
comment
connect.microsoft.com/VisualStudio/feedback/details/532300/   -  person mlvljr    schedule 10.05.2012
comment
Хорошая ссылка. Кажется, я не единственный, кто нашел это.   -  person Patrick    schedule 10.05.2012
comment
Ага, я только что написал какой-то (теперь уже явно) ошибочный код stl::set и после часа отладки того-то и того-то пошел гуглить, приходя то туда, то сюда :)   -  person mlvljr    schedule 11.05.2012


Ответы (4)


Итератор должен дать вам ссылку на const (и это то, что Стандарт говорит, что он должен делать), потому что изменение вещи, на которую ссылаются, разрушит действительность базовой структуры данных набора - набор не «знает», что поле вы изменение на самом деле не является частью ключа. Альтернативой является внесение изменений путем удаления и повторного добавления или использования вместо этого std::map.

person Community    schedule 26.03.2010
comment
Если бы вы нашли эту проблему в коде, написанном бывшим коллегой. Лично я бы действительно использовал use std::map, но мне кажется, что пользовательские операторы сравнения и множества не очень хорошо сочетаются друг с другом в VS2010. - person Patrick; 26.03.2010
comment
Пользовательские сравнения и наборы работают нормально, но изменяемые типы данных и наборы — нет. - person Björn Pollex; 26.03.2010

Совсем недавно мы начали переход на 2010 год, и это было самым большим препятствием, с которым мы столкнулись. К счастью, они выявили некоторые давние проблемы, когда мы меняли часть того, что составляло порядок набора.

В других случаях нашим решением было использовать mutable и объявлять методы как константы. При передаче разыменованного итератора в функцию по ссылке (либо указателю, либо ссылке) мы сделали аргумент константным, если он не изменялся.

Деннис

person Dennis    schedule 22.09.2010

Если вы не хотите удалять и добавлять заново, как предлагает Нил, вы можете сделать setTemperature const и m_temperature mutable.

person dalle    schedule 26.03.2010
comment
Только то, что я сказал в своем вопросе. Хотя это работает, я все еще не нахожу это достойным решением. - person Patrick; 26.03.2010

Предполагается, что набор возвращает константный итератор, потому что он не знает, могут ли какие-либо функции-члены изменить порядок.

Похоже, вам действительно нужна карта, на которой вы сопоставляете свой неизменный ключ (x, y) с переменной температурой.

person Alan    schedule 28.03.2010