Visual Studio 2010 - Bug in STL std::list::iterator
-
Hi,
Ich bin gerade auf äußerst merkwürdiges Verhalten im VS2010 gestoßen:folgender Code läuft in eine Access Violation, da ein nullptr dereferenziert wird:
#include <list> int main(int argc, char* argv[]) { typedef std::list<int> IntList; IntList testList; testList.push_back(1); testList.push_back(2); IntList::iterator itA = testList.begin(); itA = IntList::iterator(); return 0; }
Grund ist folgendes Stück code aus der xutility-Datei:
_Iterator_base12& operator=(const _Iterator_base12& _Right) { // assign an iterator if (_Myproxy != _Right._Myproxy) _Adopt(_Right._Myproxy->_Mycont); return (*this); }
ist der this-Iterator nicht neu erzeugt (Myproxy!=0) und der Right-Iterator neu, dann hängts.
Hintergrund ist der, dass ich an einer Stelle ein Objekt, was einen Iterator enthält, durch ein sauberes ersetze:
myObj = MyObj();
Da dieses Objekt einen benutzten Iterator enthält, kracht es bei der Zuweisung.
im VS2005 lief es noch erwartungsgemäß.
Wo reportet man sowas an MS?
Gruß,
vladEDIT: getestet ist das ganz eim Debug-Modus.
Im RElease gibts keine Exception
-
Wo reportet man sowas an MS?
-
Ich bezweifle, dass es sich hier und einen Bug handelt. Ich kann zumindest nichts im Standard finden, das garantieren würde, dass
itA = IntList::iterator();
funktioniert...
-
dot schrieb:
Ich bezweifle, dass es sich hier und einen Bug handelt. Ich kann zumindest nichts im Standard finden, das garantieren würde, dass
itA = IntList::iterator();
funktioniert...
warum?
eine Zuweisung wirst du doch kennen, oder?
ebendso wie eine Objektkonstruktion mit Default-Konstruktor.
das ist nix weiter als beides in einem.
Es wird ein (temporäres) lokales Objekt mit dem Defaultkonstruktor erzeugt und dann einer bestehenden Variable zugewiesen.
Die Konstruktion ist ja gar nicht das Problem.Das Problem ist die Implementation des Zuweisungsoperators des Iterators.
dieser dereferenziert ein Zeiger, ohne zu prüfen, ob er noch den explizit als ungültig definierten Initialwert hat.wenn dich der Konstruktor stört mach halt folgendes:
#include <list> int main(int argc, char* argv[]) { typedef std::list<int> IntList; IntList testList; testList.push_back(1); testList.push_back(2); IntList::iterator itA = testList.begin(); IntList::iterator itB; itA = itB; return 0; }
Das itB auf nix gültiges Zeigt ist unerheblich, es muss doch auch möglich sein, das nullptr-equivalent eines Iterators zuweisen zu können.
-
-.-
-
vlad_tepesch schrieb:
eine Zuweisung wirst du doch kennen, oder?
ja
vlad_tepesch schrieb:
ebendso wie eine Objektkonstruktion mit Default-Konstruktor.
natürlich
vlad_tepesch schrieb:
das ist nix weiter als beides in einem.
Ist mir bewusst.
vlad_tepesch schrieb:
Es wird ein (temporäres) lokales Objekt mit dem Defaultkonstruktor erzeugt und dann einer bestehenden Variable zugewiesen.
korrekt
vlad_tepesch schrieb:
Die Konstruktion ist ja gar nicht das Problem.
dochDer Standard sagt nirgendwo, dass list::iterator einen Default Konstruktor hat, geschweige denn dass der etwas sinnvolles tut...Kleine Korrektur: Der Standard garantiert zwar, dass list::iterator einen Default Konstruktor hat, aber du darfst einen default-konstruierten list Iterator nicht an einen anderen zuweisen. Das praktisch einzige, was du damit tun darfst, ist, einem default-konstruierten list Iterator einen anderen, gültigen Wert zuzuweisen (23.2.3 §5, 24.2.1 §5). Das hier ist also definitiv kein Bug in MSVC...
vlad_tepesch schrieb:
Das itB auf nix gültiges Zeigt ist unerheblich, es muss doch auch möglich sein, das nullptr-equivalent eines Iterators zuweisen zu können.
Genau da liegt dein Problem: Es gibt kein "nullptr-equivalent" eines list Iterators. Eine list hat einen Anfang, ein Ende und was dazwischen...
-
dot schrieb:
Kleine Korrektur: Der Standard garantiert zwar, dass list::iterator einen Default Konstruktor hat, aber du darfst einen default-konstruierten list Iterator nicht an einen anderen zuweisen. Das praktisch einzige, was du damit tun darfst, ist, einem default-konstruierten list Iterator einen anderen, gültigen Wert zuzuweisen (23.2.3 §5, 24.2.1 §5). Das hier ist also definitiv kein Bug in MSVC...
hm, hast du vielleicht einen Link auf das Dokument oder einen genauen Titel/Revision?
Ich finde irgendwie nur working drafts und deren Nummerierung scheint mit deiner nicht übereinzustimmen.Das einzige, was bei mir pasen könnte wäre 24.1 §5:
Results of most expressions are undefined
for singular values; the only exceptions are destroying an iterator that holds a singular value and the assignment of a
non-singular value to an iterator that holds a singular value. In this case the singular value is overwritten the same way
as any other value. Dereferenceable values are always non-singularAber wie soll ich dann mein Objekt, was einen Iterator enthält zurücksetzen?
klar ich könnte das Objekt vom HEap holen und jedes mal das Alte zerstören und ein neues anlegen, das wäre aber an der Stelle reichlich ineffizient.Da das Objekt hinter dem Iterator mehreren Listen zugeordnet werden kann, ist es auch niocht möglich auf einen End-Iterator zu testen (abgesehen davon, dass das Objekt mit dem Iterator-Member die Liste in der das Objekt steckt nicht kennt.
-
vlad_tepesch schrieb:
hm, hast du vielleicht einen Link auf das Dokument oder einen genauen Titel/Revision?
Ich finde irgendwie nur working drafts und deren Nummerierung scheint mit deiner nicht übereinzustimmen.http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf
vlad_tepesch schrieb:
Results of most expressions are undefined
for singular values; the only exceptions are destroying an iterator that holds a singular value and the assignment of a
non-singular value to an iterator that holds a singular value. In this case the singular value is overwritten the same way
as any other value. Dereferenceable values are always non-singularBingo
vlad_tepesch schrieb:
Aber wie soll ich dann mein Objekt, was einen Iterator enthält zurücksetzen?
Was ist das überhaupt für ein Objekt und wieso musst du es "zurücksetzen"?
-
dot schrieb:
Was ist das überhaupt für ein Objekt und wieso musst du es "zurücksetzen"?
Der Algorithmus erfordert es halt. Er ist Teil einer Echtzeit-Bildverarbeitung.
Ein Objekt wird in einer übergeordneten Schleife über mehrere Zyklen vervollständigt und irgendwann abgeschlossen und der weiteren Verarbeitun übergeben. Dannach wird das Objekt zurückgesetzt, bis es wieder gebraucht wird (als neu initialisiertes)Im groben also so:
MyObjekt tempObjekt; bool habeObjekt=false; for( jedes Pixel der Zeile ) { if( habeObjekt ){ if( Bedingung für objekt immer noch erfüllt){ // mache irgend was }else{ verarbeiteObjektWeiter(tempObjekt); habeObjekt = false; } }else{ // habe kein objekt if( bedingung für Objekt erfüllt ){ tempObjekt = MyObjekt();// initlaisiere Objekt neu habeObjekt = true; } } }
Aber der Algorithmus tut ja eigentlich nix zur Sache.
DAs Enzige was mir zur Auflösung des Problems noch einfallen würde, wäre die Verwendung eines Placement New.
Dann dürfte der Zuweisungsoperator nicht aufgerufen werden.
Allerdings muss dann auch der Destruktor des alten Objektes von Hand aufgerudfen werden.d.h.:
tempObjekt = MyObjekt();// initlaisiere Objekt neu
wird zu
// initlaisiere Objekt neu tempObjekt.~MyObjekt(); // altes Objekt zerstören new(&tempObjekt) MyObjekt(); // neues Objekt an der Speiicherstelle des alten erzeugen
womit sicherlich die wenigsten direkt was anfangen können, ohne zumindest nochmal nachzulesen