Warum ist const_iterator::value_type nicht const?
-
Wäre der const_iterator wirklich const, würde denn sowas gehen?
iter++
-
Werner Salomon schrieb:
Optimizer schrieb:
Der const-Iterator ist schlichtweg nur so gedacht, dass du den Container damit nicht strukturell beeinflusst.
Hmh!? ich war bisher immer der Meinung, dass ein const_iterator ein Iterator ist, mit dem man primär das Element nicht verändern kann.
Bei einer Manipulation am Container benötige ich immer Zugriff auf einen nicht-const-Container. Dass man dann natürlich nicht-const-iteratoren als Parameter mitgibt - z.B. bei erase - hielt ich bisher für einen Nebeneffekt, da die entsprechende begin()/end()-Methoden in diesem Fall auch nur nicht-const-iteratoren liefern.Man kann mit einem Iterator allein nie den Container selbst verändern. Sondern nur das Element auf das der Iterator zeigt. D.h. für mich bezog sich das 'const' im Iterator immer das Element und nie auf den Container.
Gruß
WernerJa, aber man kann nen iterator zum Beispiel an eine erase-Methode übergeben und aber nicht nen const_iterator.
Disclaimer: hab jetzt grad keinen C++ Compiler da.
-
Artchi schrieb:
Wäre der const_iterator wirklich const, würde denn sowas gehen?
iter++Das ist ja noch mal was anderes. Das wäre das dritte mögliche const:
(1) Container ist const
(2) Inhalt ist const (im Kontext des Iterators)
(3) Iterator ist constconst const_iterator_element_const
-
Optimizer schrieb:
(2) Inhalt ist const (im Kontext des Iterators)
Genau.. auf diesen Punkt bezog sich meine Frage. Wieso ist bei einem Iterator, bei dem auch der Inhalt const ist, der value_type nicht const?
Gruß
Werner
-
Ja, sind halt 3 mögliche const-Konzepte, und anscheinend fand man es nicht so wichtig, das 2te umzusetzen. Es ist aber wahrscheinlich auch eher so, dass du "rohe" Datenstrukturen wie ne verkette Liste nicht im public interface deiner Klasse hast. Wenn du einem Benutzer deiner Klasse Zugriff per Iterator gestatten willst, kannst du den Iterator ja ohne Probleme wrappen. Das heißt, du gibst nen eigenen Iterator-Typ zurück, der sich immer auf so nen list::const_iterator bezieht und auch noch T nur als const anbietet.
-
Kann man denn bei nem const_iterator den Wert verändern? ich dachte nicht?
-
Wann wird denn diese Sache zum Problem?
Hast Du vielleicht ein kleines Beispiel?Auf jeden Fall fände ich es schlimmer, wenn der value_type const wäre.
Beispiel:
ich möchte eine Kopie eines Wertes von einem const_iterator erstellen.
Was sollte diese funktion zurueckgeben?template<typename IterType> typename IterType::value_type getCopy(IterType constIterator) { }Dann hab ich ne Konstante Kopie toll.
Wenn ich das Glück habe und den Originaltypen kenne kann ich ihn ja jetzt nur mit hässlichem const_cast bearbeiten.
Oder liege ich jetzt total daneben?
-
Benutzersname schrieb:
Kann man denn bei nem const_iterator den Wert verändern? ich dachte nicht?
Jo, ich habs gerade mal testen können. Man kann den Wert von T nicht veärndern. Worüber beschwert sich Werner dann überhaupt??
-
Mal vom logischen gesehen: Die iterator_traits geben dir einige Hilfstypen in die Hand, damit du generische Algorithmen basteln kannst. Die beschreiben nicht das Verhalten IN deinem Iterator. Zum Beispiel:
template<typename it> typename iterator_traits<it>::value_type average(it st,it en) { typedef typename iterator_traits<it>::value_type val_t; val_t sum=val_t(); size_t count=0; for(;st!=en;++st) { sum+=*st; ++count; } return (count!=0)?sum:sum/count; }Für solche Konstruktionen benötigst du 'value_type' - wenn jetzt der const_iterator einen 'const T' angeben würde, hättest du das Problem, daß dieser Algorithmus nicht mehr mit einem konstanten Container funktionieren würde (oder müsstest einige zusätzliche Template-Basteleien auf dich nehmen, um das 'const' loswerden zu können). Im umgekehrten Fall (const_iterator::value_type ist nicht-const) hindert dich natürlich niemand daran, eine Konstante dieses Typs anzulegen.
(und den Versuch, die Daten hinter einem const_iterator zu beeinflussen, wird der Compiler dir in jedem Fall mit einem Fehler abblocken)
-
Benutzersname schrieb:
Kann man denn bei nem const_iterator den Wert verändern? ich dachte nicht?
stimmt kann man nicht. Im Standard (Kapitel 24.1 Absatz 4) steht dazu:
the result of the expression *i
(for constant iterator i) cannot be used in an expression where an lvalue is requiredoder auf deutsch: man kann einem '*i' nichts zuweisen und es nicht verändern, wenn 'i' ein const_iterator ist.
templäd schrieb:
Wann wird denn diese Sache zum Problem?
Hast Du vielleicht ein kleines Beispiel?Wenn eine Iterator-Klasse von einem anderen Iterator-Klasse abhängt. Im konkreten Fall war es sowas:
template< typename Itr, typename V = typename std::iterator_traits< Itr >::value_type > class MeinIterator : public boost::forward_iterator_helper< CompoundIterator< Itr, V>, V > { reference operator*() const { // ... -> compiler error: kann kein const T& auf T& castendas funktioniert nicht, wenn 'Itr' ein const_iterator z.B.: 'const int*' oder std::vector< int >::const_iterator ist.
Wie ich inzwischen festgestellt habe, würde das auch gelten, wenn ich den boost-Iterator-Helper nicht nutze. Dann sähe es so aus:
template< typename Itr, typename V = typename std::iterator_traits< Itr >::value_type > class MeinIterator : public std::iterator< std::forward_iterator_tag, V > { reference operator*() const { // ... -> compiler error: kann kein const T& auf T& castengenau das gleiche Problem.
Es gibt natürlich Abhilfe, man gibt alle anderen Template-Parameter von boost::forward_iterator_helper<> bzw. std::iterator<> auch an. Aber ich sehe nicht ein wieso, ich will nur den Default-Fall!?
templäd schrieb:
Auf jeden Fall fände ich es schlimmer, wenn der value_type const wäre.
Beispiel:
ich möchte eine Kopie eines Wertes von einem const_iterator erstellen.
Was sollte diese funktion zurueckgeben?template<typename IterType> typename IterType::value_type getCopy(IterType constIterator) { }Dann hab ich ne Konstante Kopie toll.
Wenn ich das Glück habe und den Originaltypen kenne kann ich ihn ja jetzt nur mit hässlichem const_cast bearbeiten.
Oder liege ich jetzt total daneben?Äh - nicht total, aber doch. Ob der Return-Typ einer Funktion const ist oder nicht ist egal. Dieser Wert kann sowieso nur noch kopiert werden. Da es ein temporäres Objekt ist, darf man darauf keine Aktionen machen, die es verändern. (Bem.: beim VC6-Compiler geht das schon - ist aber falsch) Der Return-Wert ist also implizit eh' const.
Es geht auch weniger um den Wert const T oder T an sich, sondern vielmehr um die Information, die da drinsteckt bzw. nicht mehr drin steckt.
In meinem Code könnte man leicht schreiben:const value_type& operator*() const {das wäre aber auch falsch, wenn der Ausgangs-Iterator eben kein const_iterator wäre. Dagegen kann ich aus einem evt. const-Typ mit remove_const immer den Nicht-const-Typ machen, falls das von Dir angesprochene Problem irgendwo anders auftaucht.
Gruß
Werner
-
CStoll schrieb:
Zum Beispiel:
template<typename it> typename iterator_traits<it>::value_type average(it st,it en) { typedef typename iterator_traits<it>::value_type val_t; val_t sum=val_t();Ah! das ist ein schönes Beispiel. Vielleicht stand die Idee solchen Codes bei der Entscheidung Pate.
Nur - sowas kommt meines Wissens in der ganzen STL nicht vor. Die Algorithmen stehlen sich mit einem kleinen Trick aus der Affäre. z.B.: accumulate:
template< typename I, typename T > T accumulate( I first, I last, T val ) { for( ; first != last; first ) val += *first; return val; }indem einfach der Result-Typ als Parameter mitgegeben wird.
Und der boost-Weg könnte - wie vorher schon erwähnt - so aussehen
template< typename I > std::iterator_traits< I >::value_type accumulate2( I first, I last ) { typedef boost::remove_const< typename std::iterator_traits< I >::value_type >::type mutable_type; mutable_type val = mutable_type(); for( ; first != last; first ) val += *first; return val; }in beiden Fällen käme man mit einem const T zurecht.
Gruß
Werner
@Edit: copy&paste-Fehler bei accumulate2 korrigiert
-
Werner Salomon schrieb:
Benutzersname schrieb:
Kann man denn bei nem const_iterator den Wert verändern? ich dachte nicht?
stimmt kann man nicht. Im Standard (Kapitel 24.1 Absatz 4) steht dazu:
the result of the expression *i
(for constant iterator i) cannot be used in an expression where an lvalue is requiredoder auf deutsch: man kann einem '*i' nichts zuweisen und es nicht verändern, wenn 'i' ein const_iterator ist.
Diese Passage sollte man im neuen Standard ändern (ein Defect Report hab ich auf die schnelle aber nicht gefunden). Wäre die Formulierung so richtig, dann wäre sogar ein einfaches
&*i
nicht erlaubt, das ist aber ein gängiges idiom.
Zum Topic: 322. iterator and const_iterator should have the same value type
-
camper schrieb:
Zum Topic: 322. iterator and const_iterator should have the same value type
Das ist doch mal 'ne Information
.Wenn ich das richtig verstanden habe, begründet Matt Austern in 322.) dieses Ansinnen damit, dass im Standard steht iterator_traits< const T* >::value_type ist 'T'. Das wäre zwar konsequent, aber kein tieferer Grund. Man könnte ja auch den Vorschlag machen, den Standard dahingehend zu ändern, dass iterator_traits< const T* >::value_type 'const T' ist.
Interessanter ist der Hinweis auf Topic 279. Es geht hier wohl um die Anforderung, dass jeder iterator in sein const_iterator-Pendant konvertierbar sein muss. Das lässt sich relativ leicht implementieren, wenn alle Template-Parameter gleich sind. Topic 279 lässt den value_type interessanterweise bewusst aus. Es steht leider immer wenig über die Hintergründe dabei.
Gruß
Werner