Ich habe in letzter Zeit häufig festgestellt, dass Typedefs für eine bestimmte Klasse innerhalb dieser Klasse relevant sind, d. H.
class Lorem
{
typedef boost::shared_ptr<Lorem> ptr;
typedef std::vector<Lorem::ptr> vector;
//
// ...
//
};
Diese Typen werden dann an anderer Stelle im Code verwendet:
Lorem::vector lorems;
Lorem::ptr lorem( new Lorem() );
lorems.push_back( lorem );
Gründe, warum es mir gefällt:
- Es reduziert das durch die Klassenvorlagen verursachte Rauschen,
std::vector<Lorem>
wirdLorem::vector
usw. - Es dient als Absichtserklärung - im obigen Beispiel soll die Lorem-Klasse als Referenz gezählt
boost::shared_ptr
und in einem Vektor gespeichert werden. - Es ermöglicht eine Änderung der Implementierung - dh wenn Lorem geändert werden
boost::intrusive_ptr
müsste, um zu einem späteren Zeitpunkt aufdringlich referenziert zu werden (via ), hätte dies nur minimale Auswirkungen auf den Code. - Ich denke, es sieht "hübscher" aus und ist wohl leichter zu lesen.
Gründe, warum ich es nicht mag:
- Es gibt manchmal Probleme mit Abhängigkeiten - wenn Sie beispielsweise eine
Lorem::vector
in eine andere Klasse einbetten möchten, aber nur Lorem deklarieren müssen (oder wollen) (anstatt eine Abhängigkeit von der Header-Datei einzuführen), müssen Sie am Ende die verwenden explizite Typen (zBboost::shared_ptr<Lorem>
stattLorem::ptr
), was etwas inkonsistent ist. - Es kann nicht sehr häufig und daher schwerer zu verstehen sein?
Ich versuche, mit meinem Codierungsstil objektiv umzugehen, daher wäre es gut, andere Meinungen dazu einzuholen, damit ich mein Denken ein wenig analysieren kann.
c++
coding-style
typedef
Will Baker
quelle
quelle
Genau das macht es nicht .
Wenn ich 'Foo :: Ptr' im Code sehe, habe ich absolut keine Ahnung, ob es sich um ein shared_ptr oder ein Foo * handelt (STL hat :: pointer typedefs, die T * sind, denken Sie daran) oder was auch immer. Esp. Wenn es sich um einen gemeinsam genutzten Zeiger handelt, gebe ich überhaupt kein typedef an, aber behalte die Verwendung von shared_ptr explizit im Code.
Eigentlich verwende ich kaum Typedefs außerhalb der Vorlagen-Metaprogrammierung.
Das STL-Design mit Konzepten, die in Bezug auf Elementfunktionen und verschachtelte Typedefs definiert sind, ist eine historische Sackgasse. Moderne Vorlagenbibliotheken verwenden freie Funktionen und Merkmalsklassen (vgl. Boost.Graph), da diese integrierte Typen nicht ausschließen Modellierung des Konzepts und weil es das Anpassen von Typen erleichtert, die nicht unter Berücksichtigung der Konzepte der angegebenen Vorlagenbibliotheken entworfen wurden.
Verwenden Sie die STL nicht als Grund, um dieselben Fehler zu machen.
quelle
std::allocator_traits<Alloc>
Klasse ... Sie müssen sie nicht für jeden einzelnen Allokator, den Sie schreiben, spezialisieren, da sie die Typen einfach direkt ausleihtAlloc
.Alloc
Autor ist es nicht gerade schwieriger, sichstd::allocator_traits<>
auf seinen neuen Typ zu spezialisieren, als die erforderlichen Typedefs hinzuzufügen. Ich habe die Antwort auch bearbeitet, da meine vollständige Antwort nicht in einen Kommentar passte.allocator_traits
versucht, einen benutzerdefinierten Allokator zu erstellen, muss ich mich nicht mit den fünfzehn Mitgliedern der Merkmalsklasse befassen. Ich musstypedef Blah value_type;
nur die entsprechenden Mitgliedsfunktionen sagen und bereitstellen, und die Standardeinstellungallocator_traits
wird das herausfinden sich ausruhen. Schauen Sie sich außerdem Ihr Beispiel für Boost.Graph an. Ja, es wird häufig die Klasse der Merkmale verwendet ... aber die Standardimplementierung vongraph_traits<G>
einfachen AbfragenG
für eigene interne Typedefs.iterator_traits
Klasse bereitgestellt, damit Ihre generischen Algorithmen problemlos nach den entsprechenden Informationen fragen können. Dies führt wiederum standardmäßig dazu, dass der Iterator nach seinen eigenen Informationen abgefragt wird. Das lange und kurze daran ist, dass sich Merkmale und interne Typedefs kaum gegenseitig ausschließen ... sie unterstützen sich gegenseitig.iterator_traits
wurde notwendig, weilT*
es ein Modell von sein sollteRandomAccessIterator
, aber Sie können die erforderlichen Typedefs nicht einfügenT*
. Sobald wiriterator_traits
das getan hatten , wurden die verschachtelten Typedefs überflüssig, und ich wünschte, sie wären dort und dann entfernt worden. Aus dem gleichen Grund (Unmöglichkeit, interne Typedefs hinzuzufügen) wirdT[N]
das STL-Sequence
Konzept nicht modelliert , und Sie benötigen Kludges wiestd::array<T,N>
. Boost.Range zeigt, wie ein modernes Sequenzkonzept definiert werdenT[N]
kann, das modelliert werden kann, da weder verschachtelte Typedefs noch Elementfunktionen erforderlich sind.Typedefs sind diejenigen , auf denen richtlinienbasiertes Design und Merkmale in C ++ aufbauen. Die Leistungsfähigkeit der generischen Programmierung in C ++ beruht also auf Typedefs selbst.
quelle
Typdefs sind definitiv ein guter Stil. Und all deine "Gründe, die ich mag" sind gut und richtig.
Über Probleme, die Sie damit haben. Nun, die Vorwärtserklärung ist kein heiliger Gral. Sie können Ihren Code einfach entwerfen, um mehrstufige Abhängigkeiten zu vermeiden.
Sie können typedef außerhalb der Klasse verschieben, aber Class :: ptr ist so viel hübscher als ClassPtr, dass ich dies nicht tue. Es ist wie bei Namespaces wie bei mir - die Dinge bleiben im Rahmen verbunden.
Manchmal habe ich getan
Und es kann Standard für alle Domänenklassen und mit einer gewissen Spezialisierung für bestimmte sein.
quelle
Die STL erledigt diese Art von Dingen ständig - die Typedefs sind Teil der Schnittstelle für viele Klassen in der STL.
sind alle Typedefs, die Teil der Schnittstelle für verschiedene STL-Vorlagenklassen sind.
quelle
Eine weitere Abstimmung dafür ist eine gute Idee. Ich habe damit begonnen, als ich eine Simulation geschrieben habe, die sowohl zeitlich als auch räumlich effizient sein musste. Alle Werttypen hatten ein Ptr-Typedef, das als gemeinsamer Boost-Zeiger begann. Ich habe dann einige Profile erstellt und einige davon in einen Boost-Intrusive-Zeiger geändert, ohne den Code ändern zu müssen, in dem diese Objekte verwendet wurden.
Beachten Sie, dass dies nur funktioniert, wenn Sie wissen, wo die Klassen verwendet werden sollen, und dass alle Verwendungen dieselben Anforderungen haben. Ich würde dies beispielsweise nicht im Bibliothekscode verwenden, da Sie beim Schreiben der Bibliothek nicht wissen können, in welchem Kontext sie verwendet wird.
quelle
Derzeit arbeite ich an Code, der diese Art von Typedefs intensiv nutzt. Soweit ist das in Ordnung.
Aber ich habe festgestellt, dass es ziemlich oft iterative Typedefs gibt, die Definitionen auf mehrere Klassen aufgeteilt sind und man nie wirklich weiß, mit welchem Typ man es zu tun hat. Meine Aufgabe ist es, die Größe einiger komplexer Datenstrukturen zusammenzufassen, die sich hinter diesen Typedefs verbergen. Daher kann ich mich nicht auf vorhandene Schnittstellen verlassen. In Kombination mit drei bis sechs Ebenen verschachtelter Namespaces wird es dann verwirrend.
Bevor Sie sie verwenden, müssen einige Punkte beachtet werden
quelle
Ich empfehle, diese Typedefs außerhalb der Klasse zu verschieben. Auf diese Weise entfernen Sie die direkte Abhängigkeit von gemeinsam genutzten Zeiger- und Vektorklassen und können diese nur bei Bedarf einschließen. Sofern Sie diese Typen nicht in Ihrer Klassenimplementierung verwenden, sollten sie meiner Meinung nach keine inneren Typedefs sein.
Die Gründe, die Ihnen gefallen, stimmen immer noch überein, da sie durch das Typ-Aliasing durch typedef gelöst werden und nicht durch die Deklaration innerhalb Ihrer Klasse.
quelle
Wenn das typedef nur innerhalb der Klasse selbst verwendet wird (dh als privat deklariert wird), halte ich es für eine gute Idee. Aus genau den von Ihnen angegebenen Gründen würde ich es jedoch nicht verwenden, wenn die Typedefs außerhalb der Klasse bekannt sein müssen. In diesem Fall empfehle ich, sie außerhalb der Klasse zu verschieben.
quelle