Implementieren eines std :: array-like-Containers mit einer C ++ 11-Initialisierungsliste

8

Der einzige und sehr unpraktische Vorbehalt von std::array ist, dass er seine Größe nicht aus der Initialisierungsliste ableiten kann wie eingebaute C-Arrays, seine Größe muss als Vorlage übergeben werden.

Ist es möglich, einen std :: array-artigen Container (einen dünnen Wrapper um ein eingebautes C-Array) mit einer C++11 initializer_list zu implementieren?

Ich frage, weil im Gegensatz zu std :: array automatisch die Größe des Arrays aus der Initialisierungsliste abgeleitet würde, was viel bequemer ist. Zum Beispiel:

%Vor%

Wir möchten auch einen Konstruktor zur Verfügung stellen, um die Größe anzugeben, wenn keine Initialisierungsliste angegeben wurde. Zum Beispiel:

%Vor%

Dies würde den Behälter auch konsistenter mit den anderen Standardbehältern machen, z. Vektor, Deque und Liste.

Nach meinem besten Wissen ist es nicht möglich, das umwickelte C-Array z. T elems [size], muss eine konstante Größe haben und die size () -Mitgliedsfunktion von initializer_list ist nicht konstant.

Außerdem habe ich mich gefragt, ob es möglich ist, solch einen Container mit einer variadischen Vorlage zu implementieren, obwohl ich das, was ich gelesen habe, nicht für möglich halte.

    
Ricky65 13.08.2011, 12:43
quelle

4 Antworten

3

Ich denke, Sie haben hier kein Glück. Der große Vorteil von std :: array ist, dass es ein POD ist und statisch initialisiert werden kann.

Wenn Sie einen Container mit einem Konstruktor haben, der eine std :: initializer_list übernimmt, müsste er die Werte kopieren (es sei denn, es handelt sich nur um einen konstanten Verweis auf den Initialisierer, was nicht sehr nützlich ist).

    
Bo Persson 13.08.2011 19:23
quelle
2
  

Ist es möglich, einen std :: array-artigen Container (einen dünnen Wrapper um ein eingebautes C-Array) mit einer C ++ 0x initializer_list zu implementieren?

Ja , nun ja, solange du bereit bist zu schummeln. Wie Mooing Duck betont, nein, nicht einmal betrügen, unser den Compiler-Implementoren . Es ist jedoch immer noch möglich, nah genug zu kommen - es ist möglich, Initialisierungslisten und ein statisches Array zu verwenden, das vom Wrapper verborgen ist.

Dies ist ein Code, den ich für meine persönliche Toolbox geschrieben habe. Der Schlüssel besteht darin, die Größe selbst für das Array zu ignorieren und den Provider-Container damit umgehen zu lassen. in diesem Fall initializer_list , die die Größe über std::distance bereitstellen können, um so die clientseitige Größenexplikation zu vermeiden (ein Begriff, den ich gerade erfunden habe, scheint es).

Da es sich um den "irgendjemand könnte" Code handeln, keine Probleme, ihn der Öffentlichkeit "zurückzugeben"; in der Tat, ich habe die Idee von einem Experten, dessen Nick ich mich nicht an Freenodes ##c++ channel erinnere, also denke ich, dass die Anerkennung für sie ist:

* BEARBEITEN * ed:

%Vor%

Die Verwendung und Deklaration im C ++ 0x-Modus ist ziemlich einfach (nur mit GCC 4.6 in Fedora 15 getestet), aber es funktioniert mit den Einschränkungen in den externen Links oben, so ist es scheinbar undefiniert Verhalten:

%Vor%

Ich sehe jedoch nicht warum ein Compiler-Implementator eine initializer_list von Integralen sowieso als statisches Array implementiert. Ihr Anruf.

Es funktioniert nicht nur so, vorausgesetzt, Sie können #ifdef den Initialisierungskonstruktor im Pre-C ++ 0x-Modus aus dem Weg räumen, Sie können dies in pre-C ++ 0x verwenden; Obwohl die Vorabdefinition des Datenarrays als eigene Variable benötigt wird, ist es IMHO, dass es der ursprünglichen Absicht am nächsten kommt (und es hat den Vorteil, dass es verwendbar ist und keine Probleme verursacht, z. B. Probleme mit dem Geltungsbereich). (auch mit dem obigen Compiler getestet, plus Debian Wheezy's GCC):

%Vor%

There! Kein "size" Parameter irgendwo !

  

Wir möchten auch einen Konstruktor zur Verfügung stellen, um die Größe anzugeben, wenn keine Initialisierungsliste angegeben wurde.

Entschuldigung, das ist eine Funktion, die ich nicht implementiert habe. Das Problem ist, wie man den Speicher "statisch" von einer externen Quelle, vielleicht einem Zuweiser, zuweist. Angenommen, es könnte irgendwie über einen Helper functor allocate geschehen, dann wäre der Konstruktor etwa so:

%Vor%

Ich hoffe, dass dieser Code hilft, da die Frage mehr oder weniger alt ist.

    
Luis Machuca 22.12.2011 20:54
quelle
1

Wie wäre es mit diesem? Ich habe std::tuple anstelle von initializer_list verwendet, weil die Anzahl der Tupelargumente zur Kompilierzeit verfügbar ist. Die Klasse tuple_array erbt von std::array und fügt einen Vorlagenkonstruktor hinzu, der mit std::tuple verwendet werden soll. Der Inhalt des Tupels wird mithilfe eines Metaprogramms Assign in den zugrunde liegenden Arrayspeicher kopiert, der zur Kompilierungszeit einfach von N nach 0 durchläuft. Schließlich akzeptiert die Funktion make_tuple_array eine beliebige Anzahl von Parametern und erstellt eine tuple_array . Der Typ des ersten Arguments wird als Elementtyp des Arrays angenommen. Gute Compiler sollten die zusätzliche Kopie mit RVO eliminieren. Das Programm funktioniert auf g ++ 4.4.4 und 4.6.1 mit RVO.

%Vor%     
Sumant 29.12.2011 03:47
quelle
0

Ich denke, diese Frage ist wirklich ziemlich einfach. Sie benötigen einen Typ, dessen Größe der Größe eines initializer_list entspricht, mit dem er initialisiert wird.

%Vor%

Versuchen Sie Folgendes:

%Vor%

Macht das Kopieren? Im technischsten Sinn ... ja. Beim Kopieren einer Initialisierungsliste speziell wird der Inhalt jedoch nicht kopiert. Das kostet also nicht mehr als ein paar Zeigerkopien. Auch jeder C ++ - Compiler, der es wert ist, verwendet zu werden, wird diese Kopie in nichts auflösen.

Also haben Sie es: ein Array, dessen Größe bekannt ist (via std::initializer_list::size ). Die Einschränkungen sind hier:

  1. Die Größe ist zur Kompilierzeit nicht verfügbar.
  2. Das Array ist nicht änderbar.
  3. std::initializer_list ist ziemlich nackt. Es hat nicht einmal den Operator [].

Der dritte ist wahrscheinlich der nervigste. Aber es ist auch leicht behoben:

%Vor%

Dort; Problem gelöst. Der Initialisiererlistenkonstruktor stellt sicher, dass Sie einen direkt aus einer Initialisierungsliste erstellen können. Und während die Kopie nicht mehr entfernt werden kann, kopiert sie immer noch nur ein paar Zeiger.

    
Nicol Bolas 29.12.2011 20:35
quelle