typedef in Verbindung mit Forward-Deklarationen... + Stilfragen



  • Hallo,

    Tut mir Leid, dass das Topic so schwammig ist, ich weiß nicht genau, wo der Fehler liegt.

    Und zwar habe ich folgenden Code:

    // Header
    
    // Forward Declarations
    class Token;
    
    class Lexer
    {
    public:
    typedef Token* TokenPtr;
    
    private:
    vector<TokenPtr> tokens_;
    
    public:
    const vector<TokenPtr>& Tokenize(string term);
    };
    
    // Quellcodedatei
    
    #include "Lexer.hpp"
    #include "Token.hpp"
    
    const vector<Lexer::TokenPtr>& Lexer::Tokenize(std::string term)
    {
    // [...]
    tokens_.push_back(new Token(/*...*/)); // Fehlerzeile !
    

    mit folgender Fehlermeldung:

    c:\projekte\krechner 2.0\lexer.cpp(150) : error C2664: 'std::vector<_Ty>::push_back': Konvertierung des Parameters 1 von 'KRechner::Token *' in 'const KRechner::Lexer::TokenPtr &' nicht möglich
    

    Wenn ich statt der Forward Declaration einfach Token.hpp inkludiere im Header, funktioniert das. Ist durch das typedef TokenPtr als Zeiger auf das leere Token* definiert? Ist typedef nicht nur ein Makro, sodass er dann einfach schaut, was Token jetzt gerade ist?

    Ich habe ein paar Fragen:

    1. Erstmal natürlich, was das Problem mit der Forward Declaration ist.
    2. Ist das stilistisch gut, im Header alles nur forward zu deklarieren und in der Quellcodedatei dann zu inkludieren? Spart ja Compile-Zeit und im Header muss eigentlich auch noch nicht der genaue Aufbau klar sein (außer in Templateklassen).
    3. Ist es gut und üblich Bla* mit BlaPtr zu typedeffen? Macht den Code nachher ja irgendwie schlanker und die boost-Zeiger haben ja auch solche Namen.
    4. Wie soll ich in der Quellcodedatei den Funktionskopf schreiben?
      a) const vectorLexer::TokenPtr& Lexer::Tokenize(std::string term)
      b) const vector<Token*>& Lexer::Tokenize(std::string term)

    b) ist kürzer, a) richtet sich aber stets nach dem Lexer-Typ, der ja z.B. später auch const werden kann oder sich sonst irgendwie ändern könnte.

    Vielen Dank im Voraus 🙂



    1. sollte kein Problem sein... dein Fehler lautet "cannot convert parameter"
      http://msdn.microsoft.com/en-us/library/s5b150wd(VS.71).aspx

    2. kA, aber wenn du es vermeiden könntest und Token vor Lexer deklarieren und definieren könntest, solltest du das lieber machen.

    3. Bla* finde ich klarer. Hinter BlaPtr kann sich ja alles verstecken. (ja ich kann die typedef nachschlagen, aber das nervt auf Dauer doch eher 😃 ) Überleg mal ein anderer schaut dein Projekt an. Der kennt sich ja nicht aus!
      Und wenn du das noch in "const Token*" machen willst solltest du es eh nicht TokenPtr nennen, sondern constTokenPtr!

    4. Ich hätte lieber das hier genommen:

    const vector< Token* >& Lexer::Tokenize(std::string term)
    

    Finde ich persönlich einleuchtender als die typedef.

    Zum Fehler:
    push_back will eine Referenz und keinen Pointer:

    void push_back ( const T& x );
    

    http://www.cplusplus.com/reference/stl/vector/push_back/

    Das sollte unabhängig von der typedef sein... Probier mal das

    ... (new Token(...) )...
    

    vorzuziehen:

    Token* mytoken = new Token(...);
    tokens_.push_back(*mytoken); //dereferenziert
    

    Wäre jetzt meine Idee... Ansonsten wüsste ich nichts mehr 😃



  • Na ja, das Ding will halt eine Referenz auf einen Pointer. Er fragt ja nach TokenPtr, was Token* sein sollte.

    Die Konvertierung von Token* auf Token*& scheint demnach das Problem zu sein, was sich aber nur daraus ergibt, dass Token* und TokenPtr hier halt doch ein Unterschied ist.

    Was sich hinter TokenPtr anderes verstecken soll als Token*, weiß ich jetzt auch nicht. 🙂 Wenn ich nichts typedeffe, hab ich halt ständig soooo lange Funktionsrümpfe. Ich meine, das ist jetzt ja noch ok, aber wenn ich zwei- oder dreifach geschachtelte Templates habe, wird's ziemlich unleserlich. Der Standard verwendet doch auch viele typedefs und in verschiedenen professionellen Anwendungen werden doch auch derartige Konventionen genutzt, oder nicht?


  • Mod



  • </Exit> schrieb:

    1. Ich hätte lieber das hier genommen:
    const vector< Token* >& Lexer::Tokenize(std::string term)
    

    Finde ich persönlich einleuchtender als die typedef.

    Naja typedefs sind aus meiner sicht ganz klar zu bevorzugen, da diese die Software wartbaerer macht. Zumal es den Anwender nicht Interresiert was für ein Pointer verwendet wird es ist nur Interresant das ein pointer verwendet wird aber nicht ob dieser Intern ein roher Zeiger ist oder ein SmartPointer.
    Zum Thema Wartung, stell dir vor du möchtes plötzlich anstelle von deinen einfachen Zeigern SpmartPointer verwenden, weil es immer schwerer wird sich um die richtige freigabe der Daten zu kümmern. Ohne typedefs ist das ein großer Aufwand diese zu ersetzen, vorallem wenn deine Klasse nicht ganz klein und trivial ist und zudem noch abhängigkeiten mit anderen Klassen hat oder um außerhalb der Klasse mit dem Zeiger zu arbeiten. Im Fall von typedefs muss ich nur eine stelle im Quellcode ändern umd die Eigenschaften des Pointers zu ändern. Ohne tpyedefs muss ich die Textersetzung bemühen und hoffen das ich alle Pointer ersetzt hab.



  • Ja, genau, das waren auch meine Gedanken. Aber das Problem ist jetzt nach wie vor nicht gelöst 😞

    Ich kann das typedef auch nicht in der Funktionsdefinition nutzen, weil er mir sagt, dass das kein Typ ist (weil sich das typedef ja auf die Forward-Deklaration bezog und nicht auf den Typ der inkludierten Datei).



  • Kannst du mal ein kompilierbares Minimalbeispiel erstellen? Denn irgendwie passt die Fehlermeldung imo nicht ganz zum Code..

    2. Ja, siehe auch hier

    3. Ja. Nicht nur, dass es lesbarer werden kann, sondern eben auch besser wartbar.

    4. Variante a, da, wenn du jetzt wirklich etwas ändern solltest, dann würdest mit Variante b vom typedef nichts gewonnen haben.

    Zu 1 kann ich nur sagen, dass das mit Visual Studio funktioniert. Könnte aber theoretisch auch sein, dass da (obwohl Spracherweiterungen abgeschaltet sind) zugunsten des Lookups vom Standard abgewichen wird. (müsste ich auch zuerst nachschauen).



  • Wie schon mehrfach erläutert: Der Code passt zu dem Ding.

    TokenPtr ist hier nicht gleich Token* sondern gleich der Variante von Token, die nur der Forward-Deklaration entspricht. Das ist meine Einschätzung der Lage, weil die Ersetzung der Forward-Deklaration durch ein Include den Fehler behebt.

    Werde später/die Tage dann Mal ein kleines lauffähiges (oder halt eben nicht) Programm posten, aber eigentlich sollte das keine neuen Erkentnisse liefern.



  • GAMES1990 schrieb:

    Im Fall von typedefs muss ich nur eine stelle im Quellcode ändern umd die Eigenschaften des Pointers zu ändern. Ohne tpyedefs muss ich die Textersetzung bemühen und hoffen das ich alle Pointer ersetzt hab.

    Hm, ja seh ich ein... Kommt halt drauf an, was man für eine Anwendung hat 😉

    @Eisflamme:
    Musst du denn die Forward-Deklaration wirklich machen? Wenn du so viele Probleme hast, dann würde ich überlegen, es nicht komplett davor zu ziehen bzw. den Header von Token zu includieren.
    Oder spricht da was in deinem Projekt dagegen? (ich kenn's ja nicht 😃 )


  • Mod

    Eisflamme schrieb:

    Wie schon mehrfach erläutert: Der Code passt zu dem Ding.

    Aber es ist nicht das Ding, folglich kann niemand etwas nachvollziehen. Weil das so ist, haben wir ernsthafte Zweifel.

    // Token.hpp
    class Token
    {
    };
    
    //Lexer.hpp
    #include <vector>
    #include <string>
    using namespace std;
    class Token;
    
    class Lexer
    {
    public:
    	typedef Token* TokenPtr;
    
    private:
    	vector<TokenPtr> tokens_;
    
    public:
    	const vector<TokenPtr>& Tokenize(string term);
    };
    
    //Lexer.cpp
    #include "Lexer.hpp"
    #include "Token.hpp"
    
    const vector<Lexer::TokenPtr>& Lexer::Tokenize(std::string term)
    {
    	tokens_.push_back(new Token());
    	return tokens_;
    }
    

    Keine Probleme hier. Also: Poste Code, der hinreichend vollständig ist.



  • Eisflamme schrieb:

    Werde später/die Tage dann Mal ein kleines lauffähiges (oder halt eben nicht) Programm posten, aber eigentlich sollte das keine neuen Erkentnisse liefern.

    Doch, wird es. Entweder wir finden den Fehler oder können dir sagen, dass du einen Fehlerhaften Compiler hast.


  • Mod

    Möglicherweise erfolgt die Vorwärtsdeklaration von Token im echten Programm im falschen Namensraum. Das ist allerdings nur geraten.



  • Ahhh... Ok, das mit der Forwarddeklaration im falschen Namespace war sogar sehr gut geraten. 😃

    Und mein anderes Problem hing dann damit zusammen, dass ich wieder typename vergessen habe anzugeben. Ich code einfach zu selten 😞 Danke! 🙂



  • camper schrieb:

    Möglicherweise erfolgt die Vorwärtsdeklaration von Token im echten Programm im falschen Namensraum. Das ist allerdings nur geraten.

    Das war genau das, was ich gemeint habe, dass der Fehler nicht zum Code passt..



  • Ok, dann geb ich Dir auch da Recht. 🙂 Ich glaube, ich hatte vorher eine andere Fehlermeldung und hab die dann irgendwie damit vermischt. Keine Ahnung.



  • Eisflamme schrieb:

    Ok, dann geb ich Dir auch da Recht. 🙂 Ich glaube, ich hatte vorher eine andere Fehlermeldung und hab die dann irgendwie damit vermischt. Keine Ahnung.

    Merke: Wenn du Code postest dann nicht welchen der so ähnlich aussieht wie der der den Fehler produziert, sondern reduziere deinen Code und stelle sicher, dass der Fehler darin auch noch vorkommt. Siehe Link in meiner Singatur.



  • Ok, ihr habt völlig Recht, tut mir Leid. Werde es in Zukunft so wie in den Regeln beschrieben machen. 🙂 Danke für die Zeit und Hilfe.


Anmelden zum Antworten