2010-03-26 16 views
11

Począwszy od Visual Studio 2010, iteracja po zestawie wydaje się zwracać iterator, który usuwa dane jako "const data" zamiast nie const.Referencje const podczas dereferencji iteratora w zestawie, począwszy od Visual Studio 2010

Poniższy kod jest przykładem czegoś, co kompiluje się na Visual Studio 2005, ale nie na 2010 (jest to sztuczny przykład, ale wyraźnie ilustruje problem, który znaleźliśmy na naszym własnym kodzie).

W tym przykładzie mam klasę, która przechowuje pozycję wraz z temperaturą. Definiuję operatory porównania (nie wszystkie, tylko tyle, aby zilustrować problem), które wykorzystują tylko położenie, a nie temperaturę. Chodzi o to, że dla mnie dwa przypadki są identyczne, jeśli pozycja jest identyczna; Nie dbam o temperaturę.

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

W głównej procedurze mam zestaw, do którego dodaję punkty. Aby sprawdzić poprawność operatora porównania, wielokrotnie dodaję punkty danych o tej samej pozycji. Pisząc zawartość zestawu, wyraźnie widzę, że w zestawie są tylko 3 punkty.

Pętla for-loop nad zestawem i ustawia temperaturę. Logicznie jest to dozwolone, ponieważ temperatura nie jest używana w operatorach porównania.

Ten kod kompiluje się poprawnie w Visual Studio 2005, ale daje błędy kompilacji w Visual Studio 2010 na następnej linii (w pętli for-):

DataPoint &point = *it; 

Błąd podane jest, że nie można przypisać "const DataPoint" do [non const] "DataPoint &".

Wygląda na to, że nie masz przyzwoitego (= nie-brudnego) sposobu pisania tego kodu w VS2010, jeśli masz operatora porównania, który porównuje tylko części elementów danych.

Możliwe rozwiązania to:

  • Dodawanie const odlewu do linii, gdzie daje błąd
  • temperaturę Making zmienny i podejmowania setTemperature const metoda

Ale dla mnie oba rozwiązania wydaje się raczej "brudny".

Wygląda na to, że komitet norm C++ przeoczył tę sytuację. Albo nie?

Jakie są czyste rozwiązania, aby rozwiązać ten problem? Czy niektórzy z was napotkali ten sam problem i jak go rozwiązaliście?

Patrick

+1

http://connect.microsoft.com/VisualStudio/feedback/details/532300/std-set-t-iterator-and-std-set-t-const-iterator-are-the- ten sam typ-breaks-code – mlvljr

+1

Ładny link. Wygląda na to, że nie jestem jedynym, który to znalazł. – Patrick

+0

Tak, właśnie napisałem jakiś (teraz wyraźnie) błędny kod stl :: set i po godzinie debugowania to-i-to poszło w wyszukiwarkę, przychodząc tu i tam :) – mlvljr

Odpowiedz

13

Iterator powinny dać const odniesienia (i to standard mówi, że powinniśmy to zrobić), ponieważ zmiany rzeczą, o której mowa zniszczy ważność podstawowej struktury danych zestawu, - zestaw nie robi” t "wiem", że zmienione pole nie jest w rzeczywistości częścią klucza. Alternatywą jest wprowadzanie zmian przez usuwanie i ponowne dodawanie lub używanie std :: map.

+0

Jeśli znalazłem ten problem w kodzie napisanym przez kolega. Osobiście użyłbym rzeczywiście std :: map, ale dla mnie wygląda na to, że operatory porównywania niestandardowego i zestawy nie pasują do siebie w VS2010. – Patrick

+4

Niestandardowe porównanie i zestawy idą dobrze, ale zmienne typy danych i zestawy nie. –

0

Jeśli nie chcesz usunięcie i ponowne dodanie jak Neil sugeruje można zrobić setTemperatureconst i m_temperaturemutable.

+0

Właśnie to, co powiedziałem w moim pytaniu. Chociaż to działa, nadal nie uważam tego za przyzwoite rozwiązanie. – Patrick

0

Zestaw ma zwrócić iterator const, ponieważ nie wie, czy jakiekolwiek funkcje członkowskie mogą zmienić kolejność.

Wygląda na to, że naprawdę chcesz mapę, na której mapujesz swój niezmienny klucz (x, y) na zmienną temperaturę.

1

Niedawno rozpoczęła się nasza konwersja na rok 2010 i była to największa przeszkoda, z jaką mieliśmy do czynienia. Na szczęście ujawnili kilka długotrwałych problemów, w których zmienialiśmy część tego, co składa się na porządek w secie.

W innych przypadkach naszym rozwiązaniem było użycie metod zmiennoprzecinkowych i zadeklarowanych jako const. Podczas przekazywania iteratora dereferencyjnego do funkcji przez odniesienie (wskaźnik lub odwołanie), wprowadziliśmy argument consst, jeśli nie był on zmieniany.

Dennis