(Hinweis: Bei dieser Frage geht es darum, die Anzahl der Elemente nicht angeben zu müssen und dennoch zu ermöglichen, dass verschachtelte Typen direkt initialisiert werden.) In
dieser Frage werden die Verwendungszwecke für ein C-Array wie erläutert int arr[20];
. In seiner Antwort zeigt @James Kanze eine der letzten Hochburgen von C-Arrays, seine einzigartigen Initialisierungseigenschaften:
int arr[] = { 1, 3, 3, 7, 0, 4, 2, 0, 3, 1, 4, 1, 5, 9 };
Wir müssen die Anzahl der Elemente nicht angeben, hurra! Durchlaufen Sie es jetzt mit den C ++ 11-Funktionen std::begin
und std::end
von <iterator>
( oder Ihren eigenen Varianten ), und Sie müssen nicht einmal an seine Größe denken.
Gibt es nun (möglicherweise TMP) Möglichkeiten, dasselbe zu erreichen std::array
? Die Verwendung von Makros erlaubt es, es schöner aussehen zu lassen. :) :)
??? std_array = { "here", "be", "elements" };
Bearbeiten : Zwischenversion, zusammengestellt aus verschiedenen Antworten, sieht folgendermaßen aus:
#include <array>
#include <utility>
template<class T, class... Tail, class Elem = typename std::decay<T>::type>
std::array<Elem,1+sizeof...(Tail)> make_array(T&& head, Tail&&... values)
{
return { std::forward<T>(head), std::forward<Tail>(values)... };
}
// in code
auto std_array = make_array(1,2,3,4,5);
Und verwendet alle Arten von coolen C ++ 11-Sachen:
- Variadische Vorlagen
sizeof...
- rWertreferenzen
- perfekte Weiterleitung
std::array
, Na sicher- einheitliche Initialisierung
- Weglassen des Rückgabetyps mit einheitlicher Initialisierung
- Typinferenz (
auto
)
Ein Beispiel finden Sie hier .
Wie @Johannes im Kommentar zur Antwort von @ Xaade ausführt, können Sie verschachtelte Typen mit einer solchen Funktion jedoch nicht initialisieren. Beispiel:
struct A{ int a; int b; };
// C syntax
A arr[] = { {1,2}, {3,4} };
// using std::array
??? std_array = { {1,2}, {3,4} };
Außerdem ist die Anzahl der Initialisierer auf die Anzahl der von der Implementierung unterstützten Funktions- und Vorlagenargumente beschränkt.
TMP
Ihre Frage?Antworten:
Das Beste, was ich mir vorstellen kann, ist:
Dies erfordert jedoch, dass der Compiler NRVO ausführt und dann auch die Kopie des zurückgegebenen Werts überspringt (was ebenfalls legal, aber nicht erforderlich ist). In der Praxis würde ich erwarten, dass jeder C ++ - Compiler dies so optimieren kann, dass es so schnell wie eine direkte Initialisierung ist.
quelle
make_array
Anruf entfernen .static_assert
und etwas TMP erfolgen, um zu erkennen, wannTail
nicht implizit konvertierbarT
ist und dann verwendet wirdT(tail)...
, aber das bleibt übrig als Übung für den Leser :)Ich würde eine einfache erwarten
make_array
.quelle
std::array<ret, sizeof...(T)>
auf derreturn
Anweisung. Dies zwingt einen Verschiebungskonstruktor für den Array-Typ sinnlos dazu,T&&
in C ++ 14 und C ++ 11 zu existieren (im Gegensatz zu einem Konstrukt aus ).Hier ist eine Lösung, die einige Ideen aus früheren Beiträgen kombiniert und auch für verschachtelte Konstruktionen funktioniert (getestet in GCC4.6):
Seltsamerweise kann der Rückgabewert nicht zu einer r-Wert-Referenz gemacht werden, die für verschachtelte Konstruktionen nicht funktioniert. Wie auch immer, hier ist ein Test:
(Für die letzte Ausgabe verwende ich meinen hübschen Drucker .)
Lassen Sie uns die Typensicherheit dieser Konstruktion verbessern. Wir brauchen definitiv alle Typen, um gleich zu sein. Eine Möglichkeit besteht darin, eine statische Zusicherung hinzuzufügen, die ich oben bearbeitet habe. Die andere Möglichkeit besteht darin, nur zu aktivieren,
make_array
wenn die Typen gleich sind, wie folgt :In jedem Fall benötigen Sie das Merkmal vom
all_same<Args...>
Typ Variadic . Hier ist sie , verallgemeinert ausstd::is_same<S, T>
(beachten Sie, dass abklingende wichtig ist , damit die VermischungT
,T&
,T const &
etc.):Beachten Sie, dass die
make_array()
Rückgabe per Kopie des Temporären erfolgt, die der Compiler (mit ausreichenden Optimierungsflags!) Als rWert behandeln oder auf andere Weise optimieren darf, undstd::array
es sich um einen Aggregattyp handelt, sodass der Compiler die bestmögliche Konstruktionsmethode auswählen kann .Beachten Sie schließlich, dass Sie die Kopier- / Verschiebungskonstruktion beim
make_array
Einrichten des Initialisierers nicht vermeiden können . Hatstd::array<Foo,2> x{Foo(1), Foo(2)};
also keine Kopie / Verschiebung, sondernauto x = make_array(Foo(1), Foo(2));
zwei Kopien / Verschiebungen, an die die Argumente weitergeleitet werdenmake_array
. Ich glaube nicht, dass Sie das verbessern können, weil Sie eine Liste mit variablen Initialisierern nicht lexikalisch an den Helfer übergeben und Typ und Größe ableiten können - wenn der Präprozessor einesizeof...
Funktion für verschiedene Argumente hätte, könnte dies möglicherweise getan werden, aber nicht innerhalb der Kernsprache.quelle
Die Verwendung der Trailing-Return-Syntax
make_array
kann weiter vereinfacht werdenLeider erfordert es für Aggregatklassen eine explizite Typspezifikation
Tatsächlich ist diese
make_array
Implementierung im Operator sizeof ... aufgeführtc ++ 17 Version
Dank des Abzugs von Vorlagenargumenten für den Vorschlag für Klassenvorlagen können wir Abzugsleitfäden verwenden, um den
make_array
Helfer loszuwerdenKompiliert mit
-std=c++1z
Flag unter x86-64 gcc 7.0quelle
Ich weiß, dass es einige Zeit her ist, seit diese Frage gestellt wurde, aber ich bin der Meinung, dass die vorhandenen Antworten immer noch einige Mängel aufweisen, daher möchte ich meine leicht modifizierte Version vorschlagen. Im Folgenden sind die Punkte aufgeführt, von denen ich denke, dass einige vorhandene Antworten fehlen.
1. Sie müssen sich nicht auf RVO verlassen
Einige Antworten erwähnen, dass wir uns auf RVO verlassen müssen, um das Konstruierte zurückzugeben
array
. Das ist nicht wahr; Wir können die Initialisierung der Kopierliste verwenden , um sicherzustellen , dass niemals temporäre Elemente erstellt werden. Also statt:wir sollten tun:
2. Machen Sie
make_array
eineconstexpr
FunktionAuf diese Weise können wir konstante Arrays zur Kompilierungszeit erstellen.
3. Sie müssen nicht überprüfen, ob alle Argumente vom gleichen Typ sind
Wenn dies nicht der Fall ist, gibt der Compiler ohnehin eine Warnung oder einen Fehler aus, da die Listeninitialisierung keine Einschränkung zulässt. Zweitens sollten wir, selbst wenn wir uns wirklich dazu entschließen, unser eigenes
static_assert
Ding zu machen (vielleicht um eine bessere Fehlermeldung zu liefern), wahrscheinlich eher die verfallenen Typen der Argumente als die rohen Typen vergleichen. Beispielsweise,Wenn wir einfach sind ,
static_assert
dass inga
,b
undc
haben die gleiche Art, dann wird diese Überprüfung fehlschlagen, aber das ist wahrscheinlich nicht das, was würden wir erwarten. Stattdessen sollten wir ihrestd::decay_t<T>
Typen vergleichen (die alleint
s sind).4. Leiten Sie den Array-Werttyp ab, indem Sie die weitergeleiteten Argumente ablehnen
Dies ähnelt Punkt 3. Verwenden Sie dasselbe Code-Snippet, geben Sie diesmal jedoch den Werttyp nicht explizit an:
Wir wollen wahrscheinlich eine machen
array<int, 3>
, aber die Implementierungen in den vorhandenen Antworten scheitern wahrscheinlich alle daran. Was wir tun können, ist, anstatt a zurückzugebenstd::array<T, …>
, a zurückzugebenstd::array<std::decay_t<T>, …>
.Dieser Ansatz hat einen Nachteil: Wir können keinen
array
cv-qualifizierten Werttyp mehr zurückgeben. Aber die meiste Zeit, anstatt so etwas wie einarray<const int, …>
, würden wirconst array<int, …>
sowieso ein verwenden. Es gibt einen Kompromiss, aber ich denke einen vernünftigen. Das C ++ 17 verfolgtstd::make_optional
auch diesen Ansatz:Unter Berücksichtigung der oben genannten Punkte
make_array
sieht eine voll funktionsfähige Implementierung von in C ++ 14 folgendermaßen aus:Verwendung:
quelle
C ++ 11 unterstützt diese Art der Initialisierung für (die meisten?) Standardcontainer.
quelle
std::vector<>
benötigt keine explizite Ganzzahl, und ich bin mir nicht sicher, warumstd::array
.{...}
Syntax impliziert eine konstante Ausdehnung zur Kompilierungszeit, sodass der ctor in der Lage sein sollte, die Ausdehnung abzuleiten.std::initializer_list::size
ist keineconstexpr
Funktion und kann daher nicht so verwendet werden. Es gibt jedoch Pläne von libstdc ++ (der Implementierung, die mit GCC ausgeliefert wird), ihre Version zu habenconstexpr
.(Lösung von @dyp)
Hinweis: erfordert C ++ 14 (
std::index_sequence
). Obwohl manstd::index_sequence
in C ++ 11 implementieren könnte .quelle
make_array
wie in Puppys Antwort.17 ++ 17 kompakte Implementierung.
quelle
Wenn std :: array keine Einschränkung ist und Sie Boost haben, schauen Sie sich das an
list_of()
. Dies entspricht nicht genau der gewünschten Array-Initialisierung vom Typ C. Aber nah dran.quelle
Erstellen Sie einen Array-Maker-Typ.
Es wird überladen
operator,
, um eine Ausdrucksvorlage zu generieren, die jedes Element über Referenzen mit dem vorherigen verkettet.Füge hinzu ein
finish
freie Funktion hinzu, die den Array-Hersteller übernimmt und ein Array direkt aus der Referenzkette generiert.Die Syntax sollte ungefähr so aussehen:
Es erlaubt keine
{}
basierte Konstruktion, wie nuroperator=
. Wenn Sie bereit sind zu verwenden=
, können wir es zum Laufen bringen:oder
Keine dieser Lösungen scheint gut zu sein.
Durch die Verwendung von Variardics wird die vom Compiler festgelegte Anzahl von Varargs und Blöcken auf die rekursive Verwendung
{}
für Unterstrukturen begrenzt.Am Ende gibt es wirklich keine gute Lösung.
Was ich tue, ist, dass ich meinen Code so schreibe, dass er sowohl
T[]
als auchstd::array
Daten unabhängig voneinander verbraucht - es ist mir egal, welchen ich ihn füttere. Manchmal bedeutet dies, dass mein Weiterleitungscode[]
Arrays sorgfältigstd::array
transparent in s umwandeln muss .quelle