2016-04-13 19 views
2

Ich weiß, dass man nicht eine virtuelle Vorlage Mitglied Funktion haben kann, aber ich möchte etwas ähnlich wie es funktioniert.Virtuelle Vorlage Mitglied Funktion

Betrachten Sie den folgenden Pseudo-Code:

struct abstract 
{ 
    template<typename T> 
    virtual T get() const = 0; 
}; 

using abstract_pointer = std::shared_ptr<abstract>; 

struct concrete_int : public abstract 
{ 
    template<> 
    int get() const { return 123; } 
}; 

struct concrete_string : public abstract 
{ 
    template<> 
    std::string get() const { return "abc"; } 
}; 

abstract_pointer factory() 
{ 
    // Some logic here to decide what concrete type to return 
    return ...; 
} 

void print_value(abstract_pointer p) 
{ 
    // Will print either 123 or "abc" 
    std::cout << "p = " << p->get() << '\n'; 
} 

int main() 
{ 
    abstract_pointer p = factory(); 

    print_value(p); 
} 

Der Hauptcode wird nur die Art abstract_pointer verwenden, es soll nicht wirklich etwas über die konkreten Klassen kennen.

Es könnte leicht mit CRTP und Typ Abzug gelöst werden, aber dann ist es nicht wirklich möglich, das Objekt zu anderen Funktion wie im obigen Beispiel zu übergeben.

Ich könnte auch Boost Variante oder Gewerkschaften verwenden, aber dann könnte es schnell unhandlich werden, wenn mehr konkrete Klassen hinzugefügt werden. Ich könnte Boost auch verwenden, aber dann müsste ich any_cast verwenden und es wäre nicht so ... nun, schön und einfach.

Es könnte sein, dass mein Google-Fu heute schlecht ist, oder ich bin einfach zu müde, aber ich habe es nicht wirklich gefunden. Ist es möglich, so etwas zu tun und gleichzeitig flexibel genug zu bleiben, um mehr konkrete Klassen hinzuzufügen und die Oberfläche einfach und schön zu halten?


Eine kleine Erklärung über den Anwendungsfall: Dies ist ein Teil eines kleinen Lexer für einen einfachen Compiler Ich mache (nur wenn zum Spaß) zu sein, und die abstrakte Klasse im Beispiel oben ist Die Klasse "Token" und die konkreten Klassen sind spezifische Token wie "Ganzzahl-Token" oder "Zeichenfolge-Token" oder "Bezeichner-Token".

Der Grund, warum ich abstrakte/konkrete Klassen und Vererbung verwenden möchte, ist, weil ich den lexer flexibel genug machen möchte, um von mehreren Sprachen verwendet zu werden, also sollte es einfach sein, ein Kind namens "identifier token" hinzuzufügen Klasse für "Keyword-Token" und vielleicht sogar eine konkrete Klasse für jedes Keyword.

Aber vielleicht AaronI ist richtig, dass ich versuche, etwas Komplexes zu machen. Ich lasse diese Frage stehen, für den Fall, dass jemand eine gute Lösung findet oder ein gutes Duplikat findet, während ich darüber nachdenke und nachdenke, ob ich selbst etwas Passendes finden kann.

+1

Wann immer ich solche brauchte, griff ich auf CRTP zurück. Aber auch CRTP erlaubt abstrakte (nicht-templated) Basisklassen, um diese einfacher zu übergeben. Ich habe diesen Ansatz stark mit meinem [STTCL-Zustandsmaschine-Framework] (https://github.com/makulik/sttcl) verwendet. –

+1

Vielleicht Check Boost.TypeErasure? –

+0

kann das abstrakte Objekt nicht einfach den Besuch unterstützen? Es ist schwierig, die Anforderung zu verstehen, ohne den Anwendungsfall zu kennen. –

Antwort

0
struct abstract 
{ 
    virtual string get() const = 0; 
}; 

using abstract_pointer = std::shared_ptr<abstract>; 

struct concrete_int : public abstract 
{ 
    string get() const override { return "123"; } 
}; 

struct concrete_string : public abstract 
{ 
    string get() const override { return "abc"; } 
}; 

abstract_pointer factory() 
{ 
    // Some logic here to decide what concrete type to return 
    return ...; 
} 

void print_value(abstract_pointer p) 
{ 
    // Will print either 123 or "abc" 
    std::cout << "p = " << p->get() << '\n'; 
} 

int main() 
{ 
    abstract_pointer p = factory(); 

    print_value(p); 
} 

Einfach-peasy. :)

Haftungsausschluss: Code nicht vom Compiler überprüft.

+1

Sollte 'get() 'in' concrete_int' nicht 'int' anstelle von' string' zurückgeben? – Andy

+0

Einfach peinlich, aber nicht * ganz * was ich gesucht habe ... :) Ich werde es mir als Alternative durchdenken, wir werden sehen wie es am Ende ausgeht. :) –

0

Wenn Sie vorhaben, neue Schlüsselwörter als Token einzuführen und gleichzeitig generischen Smart Pointer für den Zugriff auf alle Token zu verwenden, dann wird es notwendig, dass 'token :: get()' etwas zurückgibt, das grundlegend genug ist verstanden werden durch den Code, der 'token :: get()' aufruft. Betrachte 'Token' hier als eine abstrakte Klasse.

Wenn Sie (mit 'cout') das Token über 'get()' drucken und wenn ein konkretes Token ein Objekt zurückgibt, dann wird 'cout' nicht verstehen, was dieses Objekt ist oder wie das Objekt gedruckt wird.

In diesem Fall scheint es gut zu sein, alle Tokens in einen gemeinsamen fundamentalen Typ zu konvertieren (zB 'char *' oder 'string' wie von "Cheers and hth. - Alf" vorgeschlagen).

Auch wenn das Drucken einer der Anforderung ist, dann kann folgende Funktion der abstrakten Klasse hinzugefügt:

virtual void token::print(ostream &); 

Auf diese Weise jedes konkrete Objekt selbst gedruckt werden.

Verwandte Themen