Iterator eines Containers einer Eingebetteten Klasse einer Templateklasse kann nicht deklariert werden



  • Hallo

    Hier ein kompilierbarer Minimal-Code

    #include <iostream>
    #include <vector>
    
    template <class T>
    class Outer
    {
      public :
      Outer() {std::cout << "Outer::Outer" << std::endl;};
      ~Outer() {std::cout << "Outer::~Outer" << std::endl;};
    
      class Inner
      {
        public :
          Inner() {std::cout << "Inner::Inner" << std::endl;};
          ~Inner() {std::cout << "Inner::~Inner" << std::endl;};
      };
      typedef std::vector<Inner> InnerV;
    
      InnerV m_v;
      void Test();
    
    };
    
    template <class T>
    void Outer<T>::Test()
    {
      InnerV v;
      v.push_back(Inner());
    }
    
    int main()
    {
      Outer<int> Test;
      Test.m_v.push_back(Outer<int>::Inner());
      Outer<int>::InnerV::iterator neu;
    }
    

    Außerhalb der Outer-Klasse kann ich also den Iterator neu deklarieren und auch wie erwartet verwenden.
    Wenn ich aber innerhalb der Klasse Outer das selbe will kann ich nur durch expliziete Angabe des Templatetypes wieder etwas kompilierbares bekommen

    void Outer<T>::Test()
    {
      InnerV v;
      v.push_back(Inner());
      Outer<int>::InnerV::iterator neu;
    }
    

    Versuche ich hingegen den Templateparameter zu übernehmen

    void Outer<T>::Test()
    {
      InnerV v;
      v.push_back(Inner());
      Outer<T>::InnerV::iterator neu;
    }
    

    oder gar wegzulassen

    void Outer<T>::Test()
    {
      InnerV v;
      v.push_back(Inner());
      InnerV::iterator neu;
    }
    

    bekomme ich vom MinGW-Compiler in der iterator-Zeile den Fehler

    expected ';' before "neu"

    Ich habe in den üblichen Erläuterungen zu Templates keine Erklärung oder Lösung gefunden. Was ist nun falsch?

    bis bald
    akari



  • Hi,
    die Problemstellen müssen richtig so aussehen:

    typename InnerV::iterator neu;
    typename Outer<T>::InnerV::iterator neu;
    

    Wenn du InnerV:: schreibst, ist das innerhalb der Methodendefinition der eigenen Klasse, wie Outer<T>::InnerV::, da du ja eh schon in dem Scope bist. Also können wir beide gleich betrachten 😉

    Ist ja auch klar genauso wie bei:

    class MyClass {
       public:
              class La { }
              void test() 
              {
                // beides das selbe
                La zz;
                MyClass::La zt;
              }
    };
    

    Bei Templates eben genau der selbe Fall.

    Zu dem wieso da ein typename muss.

    // iterator nicht von nem Templateparameter abhängig, da schon TemplateParameter feststeht
    Outer<int>::InnerV::iterator neu;
    // T noch unbekannt, deswegen ist iterator davon abhängig, was T sein wird
    typename Outer<T>::InnerV::iterator neu;
    // dito
    typename InnerV::iterator neu;
    

    Weiß nicht, ob du letzten Erklärung so stimmen, aber sind für mich so am logischsten 😉



  • Mir stellt sich auch gerade die Frage, ob das auch nicht ohne typename funktionieren sollte.

    Da besteht schon zwar die Template-Parameter-Abhängigkeit, aber doch nicht bei InnerV. InnerV ist immer InnerV. Also doch egal was bei Outer<T> T ist.
    Oder irre ich mich da ???

    // all the same
    Outer<int>::InnerV a;
    Outer<float>::InnerV b;
    Outer<double>::InnerV c;
    // ??
    


  • Hallo

    Danke das läßt sich nun kompilieren.
    Diesen Einsatz von typename kannte ich noch nicht, hab ich auch in den letzten Stunden suchen nicht gefunden.

    So ganz klar ist mir aber nocht nicht warum der vector sich deklarieren und benutzen läßt, ein iterator des vectors ohne nachhilfe nicht.

    bis bald
    akari



  • akari schrieb:

    So ganz klar ist mir aber nocht nicht warum der vector sich deklarieren und benutzen läßt, ein iterator des vectors ohne nachhilfe nicht.

    Jetzt wo du es sagst, fällts mir auch auf. Meinst wohl diese Stelle:

    template <class T>
    void Outer<T>::Test()
    {
      InnerV v;             // Klappt
      v.push_back(Inner());
      InnerV::iterator neu; // Klappt so nicht
    }
    

    Wenn man das so schreiben würde:

    template <class T>
    void Outer<T>::Test()
    {
      Outer<T>::InnerV v;             //typename fehlt
      v.push_back(Inner());
    }
    

    Klappt das nicht, obwohl ich ja mal behauptet habe Outer<T>::InnerV sei das selbe wie InnerV. Wahrscheinlich wird InnerV beim Instanzieren der Templates zu Outer<irgendenBEKANNTERTyp>::InnerV und somit ist da kein typename nötig.

    Wieso das bei InnerV::iterator neu; nicht funktioniert, erkläre ich mir so, dass da nochmal ne verschachtelung ist und der Compiler sonst nicht wissen kann, ob iterator wirklich ein Typ ist.

    Alles was hier schreibe bezieht sich nicht auf FEST FUNDIERTES WISSEN, ich versuche es mir eher selbst irgendwie zu erklären. Bald wird aber camper oder so auftauchen und es uns erklären 🙂



  • KasF schrieb:

    InnerV ist immer InnerV.

    InnerV muss nicht immer InnerV sein.

    template <int>
    struct Outer
    {
      typedef int Inner; //ätsch
    };
    

  • Mod

    @KasF Das ist ziemlich nah dran: typename darf bei einem unqualifizierten Namen weggelassen werden, wenn dieser zuvor im Template deklariert wurde, nicht aber, wenn du auf ihn qualifiziert verwendest - einfach deshalb, weil der Standard es so vorsieht

    ISO/IEC 14882:2003 schrieb:

    14.6 [temp.res] /6
    Within the definition of a class template or within the definition of a member of a class template, the keyword typename is not required when referring to the unqualified name of a previously declared member of the class template that declares a type. The keyword typename shall always be specified when the member is referred to using a qualified name, even if the qualifier is simply the class template name.
    [Example:
    template<class T> struct A {
    typedef int B;
    A::B b; // ill-formed: typename required before A::B
    void f(A<T>::B); // ill-formed: typename required before A<T>::B
    typename A::B g(); // OK
    };
    The keyword typename is required whether the qualified name is A or A<T> because A or A<T> are synonyms within a class template with the parameter list <T>. ]

    Die typename Regeln sind unnötig streng, was ihr Verständnis erschwert. In C++0x wird sich das bessern.



  • Ah Danke.
    Wie siehts denn hiermit aus:

    Outer<int>::InnerV::iterator neu;
    

    Schließlich greife ich auch qualifiziert darauf zu und benötige aber kein typename.
    Kommt davon das der Templateparameter schon feststeht ? Im Standard habe ich dazu nichts gefunden ...



  • Exakt,
    bei voll qualifizierten Aufrufen braucht man kein "typename", da alle Bezeichner aufgelöst werden können. Nur innerhalb einer Template-Definition, weiß der Compiler halt nicht, welchen Pseudo-Code er erstellen soll, d.h. der Programmierer muß dann halt festlegen: Typ oder Variable (und zwar für alle möglichen Template-Instanziierungen <- schreibt man das so?)



  • Achso Danke, also gibt es auch einen Unterschied zw. qualifiziert und voll qualifiziert 🙂


Anmelden zum Antworten