Reicht Vorwärtsdeklaration bei Typ in Vektor?



  • Ich dachte die reicht nur bei Zeigern und Referenzen?



  • Warum probierst du es nicht einfach selber aus? 😉

    #include <vector>
    
    struct Bar;
    
    class Foo
    {
    	std::vector<Bar> vBar;
    };
    
    struct Bar
    {
    	int x;
    	int y;
    };
    
    int main()
    {
    	Foo f;
    }
    

    Compiliert unter Windows 7 + Visual Studio 2010 Express einwandfrei.



  • Ich weiß dass es kompiliert. Ich frag mich nur warum^^


  • Mod

    Deklarierer++ schrieb:

    Ich dachte die reicht nur bei Zeigern und Referenzen?

    Die Trick ist, dass der vector<Bar> selber gar kein Bar enthält. Ein vector ist immer gleich groß, egal welchen Datentyp er enthält (außer eventuelle Spezialisierungen wie vector<bool>), daher ist das für die Klassendefinition vollkommen ausreichend. Erst wenn tatsächlich Methoden des vectors verwendet werden, die an der Definition von Bar hängen (z.B. den Konstruktor des vectors), ist diese nötig.

    Vergleiche auch:

    struct Bar;
    
    template<class T> class test
    { };
    
    class Foo {
      test<Bar> bartest;  // Geht
    }; 
    
    int main()
    {
      Foo foo;   // Geht
    }
    
    struct Bar;
    
    template<class T> class test
    {
      T t;
    };
    
    class Foo {
      test<Bar> bartest;   // Geht nicht
    }; 
    
    int main()
    {
      Foo foo;   // Erst recht nicht
    }
    
    struct Bar;
    
    template<class T> struct test
    {
      T *t;
      void method_without_T()
      {
        T* t;
      }
      void method_with_T()
      {
        T t;
      }
    };
    
    struct Foo {
      test<Bar> bartest;  // Geht
    }; 
    
    int main()
    {
      Foo foo;   // Geht
    
      foo.bartest.method_without_T();  // Geht
      foo.bartest.method_with_T();     // Geht nicht
    }
    

    Ich weiß aber gerade nicht auswendig, ob dir garantiert ist, dass die Standardcontainer keine konkreten Elemente enthalten. Es macht programmiertechnisch Sinn, daher wird das jede Implementierung so machen, aber für harte Garantien müsste mal jemand den Standard durchsuchen.



  • Deklarierer++ schrieb:

    Ich dachte die reicht nur bei Zeigern und Referenzen?

    Kommt halt drauf an was vector benutzt. Das Template Argument sagt sagt ja nur aus mit was der generische Typ ersetzt wird. Und anscheinend benutzt vector nur Zeiger/Referenzen. Wenn vector jetzt eine Instanz von T speichern würde (direkt), dann würde es nicht kompilieren.

    template<class T>
    class foo
    {
     T* f;
    };
    
    template<class T>
    class bar
    {
     T b;
    };
    
    class blubb;
    
    int main()
    {
     foo<blubb> f; // kompiliert
     bar<blubb> b; // fehler
    }
    


  • Laut Standard ist es UB, Std-Templates mit unvollständigen Typen zu instantiieren (außer es wird explizit als Ausnahme gekennzeichnet, wie z.B. unique_ptr ). Demnach sollte das Beispiel falsch sein, auch wenn es wohl bei den meisten Compilern funktioniert.


  • Mod

    ipsec schrieb:

    Laut Standard ist es UB, Std-Templates mit unvollständigen Typen zu instantiieren (außer es wird explizit als Ausnahme gekennzeichnet, wie z.B. unique_ptr ). Demnach sollte das Beispiel falsch sein, auch wenn es wohl bei den meisten Compilern funktioniert.

    Kannst du mich auf die Stelle verweisen? Ich suche gerade selber, da ich das gleich im Hinterkopf hatte und es bestätigt haben wollte, aber ich finde es nicht.



  • SeppJ schrieb:

    Kannst du mich auf die Stelle verweisen? Ich suche gerade selber, da ich das gleich im Hinterkopf hatte und es bestätigt haben wollte, aber ich finde es nicht.

    Genau das dachte ich auch gerade.

    Interessanterweise kompiliert auch Folgendes auf MSVC und g++, obwohl Bar zum Zeitpunkt des vector -Konstruktoraufrufs unvollständig ist:

    #include <vector>
    
    struct Bar;
    
    struct Foo
    {
    	std::vector<Bar> v;
    };
    
    int main()
    {
        Foo f;
    }
    
    struct Bar {};
    


  • ipsec schrieb:

    Laut Standard ist es UB, Std-Templates mit unvollständigen Typen zu instantiieren (außer es wird explizit als Ausnahme gekennzeichnet, wie z.B. unique_ptr ). Demnach sollte das Beispiel falsch sein, auch wenn es wohl bei den meisten Compilern funktioniert.

    Kann ich mir gut vorstellen. Sonst würde das ja bereits einen Teil der Implementierung implizieren.
    Sprich vector wäre erlaubt z.B eine Kopie des Objektes zu halten, was dann dazu führen würde, dass das Beispiel nicht kompiliert, wenn der Typ nicht vollständig ist.
    Ich nehme mal an, dass es daher UB ist, aber müsste auch genau nachschauen, ob das so ist.

    EDIT:
    Ist UB laut 17.4.3.6/2



  • So ziemlich jedes Template aus der Standardbibliothek verlangt vollständig definierte Typen als Typparameter. Das steht so im ISO Text in der Einleitung für die StdLib. Das schließt sogar Dinge wie auto_ptr ein. Der kommende Standard macht hier ein paar Ausnahmen. So wird bei unique_ptr und shared_ptr explizit erwähnt, dass diese Klassentemplates unvollständige Typen unterstützen. std::vector<> gehört aber immer noch nicht dazu.


Anmelden zum Antworten