Was bedeutet "typedef" und "typename"?



  • Hallo,

    da ich hier im Forum immer sehr viel aktiv unterwegs bin, sind mir diese schlüsswörter immer über den weg gelaufen, jedoch konnte ich mir den Sinn dieser codezeilen nicht immer genau erklären. daher wollte ich euch um Hilfe fragen und ob ihr mir erklären könnt was man damit machen kann?!

    Danke und einen angenehmen Morgen



  • typedef gibt einem typen einfach einen anderen namen, so kann typen schnell einen Aussagekräftigeren Namen verpassen:

    typedef std::vector<unsigned int> Personalnummern;
    

    Auch kann man einer Klasse so mehr Informationen mitgeben. Zum Beispiel gibt die STL ihren Containern über typedefs die Information mit, welche Iteratoren verwendet werden:

    template<class T>
    class FooContainer
    {
        public:
            typedef T* Iterator;
        ...
    };
    
    //will man nun einen Iterator speichern, kann man das so machen
    typedef FooContainer<int> Container;
    typedef Container::iterator Iterator;
    
    Container container;
    ...
    Iterator iter=container.begin();
    //alternativ
    FooContainer<int> container;
    ...
    FooContainer<int>::Iterator iter=container.begin();
    

    typename kommt nun im Zusammenhang von typedefs mit templates ins spiel. Zuvor muss man wissen, dass man mit dem :: Operator (wie in Container::Iterator) sowohl zugriff auf typedefs als auch statische Variablen haben kann.

    Bei templates bekommt der Compiler ein Problem:

    template<class T>
    class Foo
    {
        private:
            typedef FooContainer<T>::Iterator Iterator;//Problem
    };
    

    Der Compiler weis an diesem Punkt nicht, was ::Iterator ist. Es könnte ein Typ oder eine statische Variable sein, und er kann das auch so ohne weiteres auch nicht herausfinden, weil FooContainer ja auch so aussehen könnte:

    template<class T>
    class FooContainer
    {
        public:
            typedef T* Iterator;
        ...
    };
    
    //spezialisierung auf int
    //dummes Beispiel incoming
    template<>
    class FooContainer<int>
    { 
        public:
            static int Iterator;
    };
    

    würde Foo<int> erstellt werden, währe FooContainer::Iterator eine statische Variable und kein typ, den typedef braucht.

    Deswegen hat man im Standard beschlossen, dass ein normales "FooContainer<T>::Iterator" per default kein typ ist sondern eine statische variable. Um nun dem Compiler zu sagen, dass das doch ein typ ist, brauchst du typename:

    template<class T>
    class Foo
    {
        private:
            typedef typename FooContainer<T>::Iterator Iterator;//Lösung
    };
    


  • otze schrieb:

    typedef gibt einem typen einfach einen anderen namen, so kann typen schnell einen Aussagekräftigeren Namen verpassen:

    ok alles klar, aber warum sollte ich datentypen unbedingt andere namen geben, ich meine, ein int ist für mich aussagekärftiger als irgend ein andere name für den datentyp. das erschließt sich mir noch nciht so ganz.

    aber schonmal vielen dank für deine erklärung



  • zB ist eine Änderung von einem Typ zum Anderen dann viel leichter.

    Und zB matrix2d ist für mich leserlicher als vector< vector< double > >.
    Oder gar matrix3x vector< vector< vector< double > > >



  • gut das is ne logische erklärung.

    das ich das jetzt richtig verstanden habe wenn ich also dem vector< vector<int> > test einen anderen namen zuweißen will kann ich dsa also so machen

    typedef vector< vector<int> > matrix2d
    

    dann kann ich also mit matrix2d auf das ganze teilzugreifen oder hab ich das falshc verstanden

    edit: also könnte ich dann sagen

    matrix2d eigentlichervector;
    
    eigentlichervector.push_back(11);
    


  • richtig verstanden 🙂

    typedefs in templates können auch verwendet werden, damit wiederum andere templates darauf zugreifen können. Beispiel:

    template <class Container>  //gibt das letzte Element eines Containers aus
    void PrintLastEntry(Container const& c) {
      if (c.size() == 0) return;
      typename Container::const_iterator it = c.begin();
      typename Container::value_type val = *it;
      while (++it != c.end()) val = *it;
      cout << val << endl;
    }
    

    Dieses Funktionstemplate ist auf alle Container anwendbar, die neben begin(), end() und size() noch die typedefs const_iterator und value_type beinhalten. Der Nutzer braucht z.B. nicht zu wissen, von welchem Typ die Iteratoren eines bestimmten STL-Containers nun wirklich sind, es könnten Pointer oder andere Konstrukte sein. Durch den typedef im Container kann der User einfach mit vector<int>::iterator den iteratortyp benennen ohne wissen zu müssen, ob bei seiner Standardbibliothek nun ein int* oder etwas komplizierteres dahintersteckt.

    /edit: und ja, ich weiß dass mein template oben recht gruselig ist, es soll auch nur den Nutzen von typedefs zeigen und nicht abgetippt werden 😛



  • danke schön, dann bin ich ja mal froh das ich das richtig umgesetzt hatte 🙂

    schönen tag noch



  • Was man auch häufig sieht ist sowas:

    typedef unsigned int tUInt
    typedef signed int tInt
    typedef unsigned short tUInt8
    typedef signed short tInt8
    ...
    

    Das Ganze dann in #ifdefs um zu schauen was für eine Plattform man hat usw.

    So kann man sicherstellen das im Code auch mit den "richtigen" Typen auf allen Plattformen gearbeitet wird, da C++ ja nur Relationen zwischen den Typen vorgibt.



  • ok und wie kann ich so eine überpürfen durchführen, welcher datentyp auf welcher plattform der richtige ist?



  • Firefighter schrieb:

    ok und wie kann ich so eine überpürfen durchführen, welcher datentyp auf welcher plattform der richtige ist?

    so wie Fellhuhn es geschrieben hat, werden die werte aus den jeweiligen tabellen der Plattform abgeschrieben.

    Eine andere Lösung läuft über template Magie wo das automatisch berechnet wird.
    Das ist aber schon ein paar ecken komplexer 😉


Anmelden zum Antworten