std::array vs plain arrays



  • Hi,

    was sind denn eigentlich die Vorteile/Nachteile von std::array im Vergleich zu normalen Arrays

    Und ist es normal, das man std::array nur mit {{ele1, ele2, ele3, ...}} intialisieren kann, nicht aber mit {ele1, ele2, ele3, ...}, oder ist das ein Bug?



  • Als Vorteile fallen mir gerade ein:

    - Kopiersemantik
    - Kein array-to-pointer-decay mehr.
    - range-checking in debug-builds.
    - Methode um die Größe abzufragen.
    - Besseres Design (finde ich).


  • Mod

    Vorteile: std::array die gleiche Semantik wie alle anderen Basisdatentypen (und die meisten komplexen Datentypen), insbesondere ist es kopierbar. Keine der Eigenheiten eines Arrays.

    Nachteile: std::array hat keine Nachteile gegenüber normalen Arrays.

    Zur Initialisierung: Das sollte auch mit einfachen Klammern gehen, da std::array (ab C++11) eine Initialisiererliste unterstützt. Der aktuelle GCC ist in dieser Hinsicht noch überkritisch, es sollte aber trotz Warnungen funktionieren. C++11 ist noch recht jung, noch nicht alle Features sind implementiert.



  • pyhax schrieb:

    Und ist es normal, das man std::array nur mit {{ele1, ele2, ele3, ...}} intialisieren kann, nicht aber mit {ele1, ele2, ele3, ...}, oder ist das ein Bug?

    Das ist normal so, weil std::array ein Array kapselt. Es ist eine stinknormale Aggregat-Initialisierung. Das äußere Klammerpaar ist für die Klasse, das innnere für das Array als Datenelement der Klasse.

    Ich habe zwar auch mal etwas von "brace elision" gehört, aber da bin ich mir jetzt nicht sicher, was das sein soll und wann es zutrifft. Vielleicht kann man das in C++11 wirklich auch mit einem Klammerpaar machen. (?)



  • <a href= schrieb:

    Dokumentation zu boost::array ">Note that for standard conforming compilers it is possible to use fewer braces (according to 8.5.1 (11) of the Standard). That is, you can initialize an array as follows:

    boost::array<int,4> a = { 1, 2, 3 };
    

  • Mod

    krümelkacker schrieb:

    pyhax schrieb:

    Und ist es normal, das man std::array nur mit {{ele1, ele2, ele3, ...}} intialisieren kann, nicht aber mit {ele1, ele2, ele3, ...}, oder ist das ein Bug?

    Das ist normal so, weil std::array ein Array kapselt. Es ist eine stinknormale Aggregat-Initialisierung. Das äußere Klammerpaar ist für die Klasse, das innnere für das Array als Datenelement der Klasse.

    Ich habe zwar auch mal etwas von "brace elision" gehört, aber da bin ich mir jetzt nicht sicher, was das sein soll und wann es zutrifft. Vielleicht kann man das in C++11 wirklich auch mit einem Klammerpaar machen. (?)

    Das ist aber ein bekannter Bug im GCC, bloß ist die Priorität extrem niedrig, da es bloß eine Warnung ist und diese meistens gerechtfertigt ist:
    http://gcc.gnu.org/bugzilla/show_bug.cgi?id=25137

    Man beachte noch zwei Dinge:
    1. Schon TR1 verlangte, dass die Initialisierung von tr1::array mit nur einer Klammer erlaubt ist, das habe ich oben fälschlich erst C++11 zugeschrieben.
    2. Absatz 8.5.1/11 im Standard (auch schon C++98) verlangt sogar explizit, dass dies für einen großen Teil aller zusammengesetzten Typen geht. Natürlich darf ein Compiler trotzdem warnen, er muss es bloß fressen und korrekten Code erzeugen, falls man es doch macht.

    edit: Ein paar kleine Korrekturen und Klarstellungen.



  • Ist dann folgendes ein Bug?

    include <array>
    
    struct IntArray {
            std::array<int, 3> arr;
            IntArray(int a, int b, int c): arr{a,b,c} {}
    };
    
    int main() {
            IntArray a(1,2,3);
    }
    

    Fehlermeldung:

    ./test.cpp:5:42: Fehler: »{a, b, c}« konnte nicht von »<geschweift geklammerte Initialisierungsliste>« nach »std::array<int, 3u>« umgewandelt werde
    

    Mit {{ funktioniert es.



  • Nein. Wenn es nicht geht, geht es nicht, denn std::array ist eben auch nur eine Klasse. Aber gibt es nicht einen Konstruktor dafür mit std::initializer_list<> ?

    Edit: Zum Konstruktor: Nein, das gäbe im Endeffekt einen Laufzeitfehler usw. da std::array ja ein statisches Array ist...

    Edit²: Hab grad im Header geschaut. Da hat std::array ja überhaupt keinen Konstruktor! Aber wie wird das Array dann kopiert?

    // No explicit construct/copy/destroy for aggregate type.
    


  • Compilergenerierte Funktionen?



  • Oberon_0 schrieb:

    Compilergenerierte Funktionen?

    😑

    Ja, aber das Array drinnen? Wird da nicht einfach das C-Array in einen Zeiger konvertiert, welcher dann kopiert wird?



  • @Hacker: std::array muss ja ein POD sein, um es so initialisieren zu können. Also auch keine eigenen Konstruktoren.


  • Mod

    pyhax schrieb:

    Ist dann folgendes ein Bug?

    include <array>
    
    struct IntArray {
            std::array<int, 3> arr;
            IntArray(int a, int b, int c): arr{a,b,c} {}
    };
    
    int main() {
            IntArray a(1,2,3);
    }
    

    Fehlermeldung:

    ./test.cpp:5:42: Fehler: »{a, b, c}« konnte nicht von »<geschweift geklammerte Initialisierungsliste>« nach »std::array<int, 3u>« umgewandelt werde
    

    Mit {{ funktioniert es.

    gcc hat recht, insofern er sich weigert, hier brace-elision zu verwenden, die ist nämlich nur bei Initialisierungen der Form

    T x = { a };
    

    zulässig (8.5.1/11). (Anm: clang führt hier brace-elision durch).

    Andererseits scheint mir hier ein std::bug vorzuliegen:
    T elems[N] ist ein exposition-only Member, also nicht Teil des Interfaces, es wird eigentlich überhaupt nicht vorausgesetzt, dass ein Array-Member für die Daten verwendet wird (wobei nat. zu bezweifeln ist, dass es irgendeine Implementation anders macht). Zum Beispiel wäre ja auch so eine Implementation denkbar (mit geeigneter Spezialisierung für N==0 und N==1):

    template <typename T, size_t N> struct array
    {
    ...
        array<T, N-1> first;
        T last;
    ...
    };
    

    wenn der Compiler korrekte Packung garantiert (wovon eine Standardbibliothek ja abhängen kann). Die korrekte vollständige Klammerung in so einem Fall sieht aber ganz anders aus:

    array<int, 4> x = {{{{0},0},0},0};
    

    Mit anderen Worten: es scheint keine Garantie zu geben, dass doppelte Klammerung korrekt ist.

    Edit: Varianten wie T elems[N][1][1][1] oder T elems[1][1][1][N] wären auch problematisch.


  • Mod

    wxSkip schrieb:

    @Hacker: std::array muss ja ein POD sein, um es so initialisieren zu können. Also auch keine eigenen Konstruktoren.

    PODs und Aggregate sind verschiedene Kategorien, die sich nur z.T. überschneiden.



  • Hacker schrieb:

    Oberon_0 schrieb:

    Compilergenerierte Funktionen?

    😑

    Ja, aber das Array drinnen? Wird da nicht einfach das C-Array in einen Zeiger konvertiert, welcher dann kopiert wird?

    Sobald ein Array in einer struct/class ist hat es stinknormale Valuesemantik und wird auch korrekt durch den compilergenerierten Zuweisungsoperator kopiert.



  • Ethon schrieb:

    Hacker schrieb:

    Oberon_0 schrieb:

    Compilergenerierte Funktionen?

    😑

    Ja, aber das Array drinnen? Wird da nicht einfach das C-Array in einen Zeiger konvertiert, welcher dann kopiert wird?

    Sobald ein Array in einer struct/class ist hat es stinknormale Valuesemantik und wird auch korrekt durch den compilergenerierten Zuweisungsoperator kopiert.

    Alles klar 👍


Anmelden zum Antworten