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ß,
    vlad

    EDIT: getestet ist das ganz eim Debug-Modus.
    Im RElease gibts keine Exception



  • Wo reportet man sowas an MS?

    Hier: http://connect.microsoft.com/



  • 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.

    doch

    Der 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-singular

    Aber 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-singular

    Bingo

    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


Anmelden zum Antworten