Ich schaue den Eigenen Quellcode für Bildungszwecke durch. Ich habe festgestellt, dass für jede konkrete Klassenvorlage X
in der Hierarchie ein internal::traits<X>
definiert ist. Ein typisches Beispiel finden Sie in Matrix.h:
Ich verstehe jetzt, dass Eigenschaften eine Möglichkeit darstellen, vorhandene Klassen zu erweitern, die Sie nicht mit zusätzlichen Informationen zu einem neuen Code ändern möchten. Zum Beispiel könnte ein Benutzer der Klassenvorlage Foo<class TAllocator>
die vorhandenen Speicherzuordner FastAlloc
und AlignedAlloc
verwenden wollen, aber Foo muss wissen, wie man mit diesen beiden interagiert, und so ein FooTraits<AlignedAlloc>::allocate()
und FooTraits<FastAlloc>::allocate()
werden vom Benutzer definiert, die wiederum von Foo
verwendet werden.
In diesem Fall sehe ich jedoch nicht ohne weiteres das Problem, nur Scalar
in jeder abgeleiteten Klasse anzugeben, d. h. Matrix
define Matrix::Scalar
mit einem typedef im Klassenkörper. Was ist der Vorteil einer Traits-Klasse? Ist es nur für den Zweck, den Code sauber zu halten, d. H. Alle relevanten Eigenschaften jeder Klasse in der Merkmalsklasse zu speichern?
Bearbeiten Sie gemäß Nicol Bolas Antwort: Ich verstehe, dass einige dieser typedefs möglicherweise "intern" gehalten werden müssen, d. h. sollten nicht dem Benutzer ausgesetzt werden, was die Merkmalsklasse erklären würde. Das scheint sinnvoll zu sein, jedoch sind einige dieser Typdefinitionen wie Scalar
, für die Außenwelt über einen typedef in der Basisklasse von Matrix
:
Das bringt uns zu der ursprünglichen Frage zurück: Warum ist Scalar
nicht nur ein typedef in Matrix
selbst? Gibt es einen Grund neben der stilistischen Wahl?
Ich vermute, dass, da die Merkmalsklasse internal
ist, dies der Punkt ist, eine Merkmalsklasse zu verwenden. Das heißt, diese Dinge intern zu behalten. Auf diese Weise hat Matrix
selbst in seiner privaten Schnittstelle nicht viele Oddball-Definitionen und so weiter.
Betrachten Sie die Aufzählung in Ihrem Beispiel. Diese "enums" (aka: static constexpr
Variablen vor C ++ 11) sehen nicht wie etwas aus, was ein Benutzer wissen sollte. Es ist ein Implementierungsdetail und daher sollte es versteckt werden.
MatrixBase
ist ein CRTP-Problem.
Siehe Matrix
würde wie folgt definiert:
Diese partielle Definition bewirkt, dass zwei Dinge passieren:
Wenn Matrix
noch nicht als Klassentyp deklariert wurde, wird es zu einer legalen Klasse, auf deren Namen verwiesen und verwendet werden kann.
Die Vorlage MatrixBase
muss mit dem Typ Matrix
instanziiert werden. Jetzt gerade .
Das Problem ist, dass "gerade jetzt", Matrix
eine unvollständige Klasse ist. Der Compiler ist noch nicht in den Rumpf dieser Definition eingetreten, so dass der Compiler nichts über seine Interna weiß. Aber MatrixBase
muss gerade instanziiert werden.
Daher kann MatrixBase
keinen der Inhalte der Klasse Derived
verwenden, für die sie bereitgestellt wird. Wenn Matrix
eine Art typedef enthält, kann MatrixBase<Derived>
sie nicht sehen
Jetzt können Elementfunktionen von MatrixBase<Derived>
Definitionen in Derived
betrachten, da diese definiert sind, nachdem die vollständige Klasse definiert wurde. Auch wenn diese Funktionen im Rahmen der Klasse definiert sind.
Sie können jedoch keine Eigenschaften von MatrixBase
access properties von Derived
haben. Daher die Charaktereigenschaften Indirection. Die Merkmalsklasse kann eine Spezialisierung basierend auf einem unvollständigen Typ verwenden, um Definitionen für MatrixBase
verfügbar zu machen.
Der Hauptgrund für diese traits
-Klasse ist, rekursive Abhängigkeiten im CRTP zu vermeiden. Ohne es würden wir mit etwas enden wie:
kann unter bestimmten Umständen nicht kompiliert werden.
Grundsätzlich erlaubt diese Klasse traits
, Base<Matrix>
vollständig zu deklarieren, ohne die Deklaration von Matrix
zu kennen.