Ich habe eine Funktion, die mehrdimensional ist std::vector
und erfordert, dass die Tiefe (oder die Anzahl der Dimensionen) als Vorlagenparameter übergeben wird. Anstatt diesen Wert fest zu codieren, möchte ich eine constexpr
Funktion schreiben , die die std::vector
und die Tiefe als unsigned integer
Wert zurückgibt .
Zum Beispiel:
std::vector<std::vector<std::vector<int>>> v =
{
{ { 0, 1}, { 2, 3 } },
{ { 4, 5}, { 6, 7 } },
};
// Returns 3
size_t depth = GetDepth(v);
Dies muss jedoch zur Kompilierungszeit erfolgen , da diese Tiefe als Vorlagenparameter an die Vorlagenfunktion übergeben wird:
// Same as calling foo<3>(v);
foo<GetDepth(v)>(v);
Gibt es eine Möglichkeit, dies zu tun?
std::vector
ist eine Laufzeitsache, keine zur Kompilierungszeit. Wenn Sie einen Container zur Kompilierungszeit benötigen, schauen Sie nachstd::array
. Ebenfalls; Beachten Sie, dassconstexpr
nur Mittel „ können bei der Kompilierung ausgewertet werden“ - es gibt kein Versprechen , dass es wird sein. Es kann zur Laufzeit ausgewertet werden.std::vector
s ineinander verschachtelt sind. Zum Beispiel mitstd::vector<std::vector<int>> v;
,GetDepth(v);
würde 2 zurückkehren , da es sich um eine 2 - dimensionale Vektor ist. Die Größe spielt keine Rolle.vector
ist nicht immer der beste Weg, um Dinge zu tun. Die manuelle 2D- oder 3D-Indizierung eines einzelnen flachen Vektors kann je nach Anwendungsfall effizienter sein. (Nur ganzzahlige Mathematik statt Zeigerjagd von den äußeren Ebenen.)rank
für diese Abfrage Array-Typen (in Übereinstimmung mit der mathematischen Nomenklatur für Tensoren). Vielleicht ist das hier ein besseres Wort als "Tiefe".Antworten:
Ein klassisches Vorlagenproblem. Hier ist eine einfache Lösung wie die C ++ - Standardbibliothek. Die Grundidee besteht darin, eine rekursive Vorlage zu haben, die jede Dimension einzeln zählt, mit einem Basisfall von 0 für jeden Typ, der kein Vektor ist.
Dann könnten Sie es so verwenden:
Bearbeiten:
Ok, ich habe die allgemeine Implementierung für jeden Containertyp abgeschlossen. Beachten Sie, dass ich einen Containertyp als alles definiert habe, das einen wohlgeformten Iteratortyp gemäß dem Ausdruck hat,
begin(t)
in denstd::begin
für die ADL-Suche importiert wird, undt
einen l-Wert vom Typ hatT
.Hier ist mein Code zusammen mit Kommentaren, um zu erklären, warum Sachen funktionieren und welche Testfälle ich verwendet habe. Beachten Sie, dass zum Kompilieren C ++ 17 erforderlich ist.
quelle
std::vector<T>
Spezialisierung buchstäblich einfach kopieren, einfügen und in einen anderen Containertyp ändern. Das einzige, was Sie brauchen, ist der 0-Basisfall für jeden Typ, auf den Sie sich nicht spezialisiert habenUnter der Annahme , dass ein Behälter jeden Typ ist, hat
value_type
unditerator
Elementtypen (Standard - Bibliothek Behälter erfüllen diese Anforderung) oder einen C-Stil - Array, können wir leicht verallgemeinern Cruz Jean ‚s - Lösung:Containertypen können bei Bedarf weiter eingeschränkt werden.
quelle
Sie können die folgende Klassenvorlage definieren, die einem
vector_depth<>
beliebigen Typ entspricht:Diese primäre Vorlage entspricht dem Basisfall, der die Rekursion beendet. Definieren Sie dann die entsprechende Spezialisierung für
std::vector<T>
:Diese Spezialisierung entspricht einem
std::vector<T>
und entspricht dem rekursiven Fall.Definieren Sie abschließend die Funktionsvorlage
GetDepth()
, die auf die obige Klassenvorlage zurückgreift:Beispiel:
Die Ausgabe dieses Programms ist:
quelle
std::vector
, aber zBGetDepth(v)
wov
wirdint
nicht kompiliert. Wäre besser zu habenGetDepth(const volatile T&)
und einfach zurückzukehrenvector_depth<T>::value
.volatile
Lässt es einfach mehr Dinge abdecken und ist maximal für den Lebenslauf qualifiziert