C++ statische Polymorphie (CRTP) und die Verwendung von typedefs von abgeleiteten Klassen
Lese ich die Wikipedia-Artikel über den neugierig-recurring template pattern in C++ tut für statische (read: compile-Zeit) - Polymorphismus. Ich wollte zu verallgemeinern, so dass ich ändern könnte, die Rückgabetypen der Funktionen, basierend auf den abgeleiteten Typ. (Dies scheint, wie es sollte möglich sein, da die base-Typ kennt die abgeleitete Typ von der template-parameter). Leider ist der folgende code wird nicht kompiliert mit MSVC 2010 (ich weiß nicht, haben Sie einfachen Zugang zu gcc jetzt, so dass ich habe nicht versucht es noch). Wer weiß, warum?
template <typename derived_t>
class base {
public:
typedef typename derived_t::value_type value_type;
value_type foo() {
return static_cast<derived_t*>(this)->foo();
}
};
template <typename T>
class derived : public base<derived<T> > {
public:
typedef T value_type;
value_type foo() {
return T(); //return some T object (assumes T is default constructable)
}
};
int main() {
derived<int> a;
}
BTW, ich habe einen work-around mit extra-template-Parameter, aber ich weiß nicht wie es---es wird sehr ausführlich bei der übergabe viele Arten die Vererbungskette.
template <typename derived_t, typename value_type>
class base { ... };
template <typename T>
class derived : public base<derived<T>,T> { ... };
EDIT:
Die Fehlermeldung, dass MSVC 2010 gibt in dieser situation ist error C2039: 'value_type' : is not a member of 'derived<T>'
g++ 4.1.2 (via codepad.org) sagt error: no type named 'value_type' in 'class derived<int>'
- Nur damit Sie wissen, codepad.org kompilieren und ausführen von code für Sie, und ich glaube, es verwendet den gcc/g++. So sind Sie nie außerhalb der Reichweite von g++ 🙂
- könntest du welche Fehler sind Sie immer so, dass ich nützlich sein für die Leser.
- Ideone verwendet gcc sicher, es ist also noch einer 🙂
- vielen Dank für den Tipp über codepad.org! @Sriram: Guter Anruf. Ich fügte Sie.
Du musst angemeldet sein, um einen Kommentar abzugeben.
derived
ist unvollständig, wenn Sie verwenden es als ein template-argument zubase
in seiner Basisklassen-Liste.Einer gemeinsamen Lösung ist die Verwendung einer traits-Klasse-Vorlage. Hier ist dein Beispiel, traitsified. Dies zeigt, wie beide Arten und Funktionen von der abgeleiteten Klasse durch die Züge.
Brauchen Sie nur, um sich zu spezialisieren
base_traits
für alle Typen, die Sie für die Vorlage verwenden-argumentderived_t
vonbase
und stellen Sie sicher, dass jede Spezialisierung bietet allen Mitgliedern, diebase
erfordert.Derived<int>
Fehler gibt, ich bin versucht, herauszufinden, warum ?main()
keine template-Instanziierung stattfindet. Bestimmte Fehler nur kommen, wenn der compiler versucht zu instanziieren und zu verwenden die Vorlage.int main() { derived<int>().base_foo(); }
auf der Unterseite meines Merkmale Beispiel (dies zwingt die Instanziierung von allem), sollte es kompilieren mit Visual C++ 2010, g++ 4.5.1, und die aktuelle Clang baut.derived
ein Standard-argument (dieser Ansatz wird nicht verlangen viel code). Ich habe geschrieben in meiner Antwort.struct derived : base<derived> {...};
macht die base_traits nicht definiert.Einem kleinen Nachteil der Verwendung von Eigenschaften ist, dass Sie haben zu erklären, eine für jede abgeleitete Klasse. Schreiben Sie eine weniger ausführliche und redondant workaround so :
In C++14 Sie können die
typedef
und benutzen Sie die Funktionauto
Rückgabetyp Abzug:Dies funktioniert, weil der Abzug der Rückgabetyp
base::foo
verzögert, bisderived_t
abgeschlossen ist.Alternative zu Typ-Eigenschaften, die erfordert weniger boilerplate wird zum nest Ihrer abgeleiteten Klasse in eine wrapper-Klasse, die hält Ihre Typdefinitionen (oder mit s), und übergeben Sie die wrapper als template-argument zu Ihrer Basisklasse.
T
werden nicht in der Lage abgeleitet werden, die in der Funktion tamplates, zum Beispieltemplate<class T> f(outer<T>::derived x)
(wie opossed zutemplate<class T> f(derived2<T> x)
.template <class C, class T = typename C::value_type> auto f(C x)
. Wenn Sie sind besorgt über die Annahme beliebiger Typen, die Sie hinzufügen könnenclass = std::enable_if_t< std::is_same_v< C, NicerName<T> >, int>
zu den template-Parameter-Liste, aber es wird ziemlich lange.Ich weiß, dass dies im Grunde ist die Problemumgehung, die Sie gefunden und weiß nicht wie, aber ich wollte, um es zu dokumentieren und auch zu sagen, dass es im Grunde die aktuelle Lösung für dieses problem.
Bin ich auf der Suche nach einem Weg, dies zu tun für eine Weile und nie eine gute Lösung gefunden.
Die Tatsache, dass es nicht möglich ist, ist der Grund, warum letztlich Dinge wie
boost::iterator_facade<Self, different_type, value_type, ...>
müssen viele Parameter.Möchten wir natürlich auch etwas, so etwas wie dies funktioniert:
Wenn dies möglich war, alle Eigenschaften der abgeleiteten Klasse weitergeleitet werden konnten implizit ot der Basisklasse. Die Redewendung fand ich den gleichen Effekt zu erhalten ist das bestehen der Eigenschaften der Basisklasse völlig.
https://godbolt.org/z/2G4w7d
Der Nachteil ist, dass das Merkmal in der abgeleiteten Klasse zugegriffen werden, mit einer qualifizierten
typename
oder wieder aktiviert vonusing
.