Array-Member initialisieren


  • Mod

    Huch, da fällt mir was viel hübscheres ein:

    template <std::size_t N, typename=std::make_index_sequence<N>>
    struct test {};
    
    template <std::size_t N, std::size_t... indices>
    struct test<N, std::index_sequence<indices...>> {
        std::array<int, N> arr{ };
        std::array<std::atomic<int*>, N> a{{&arr[indices]}...};
    };
    

    wusste gar nicht, dass {…} ein gültiges pattern ist. Falls du noch nicht mit C++14 arbeiten kannst, lässt sich make_index_sequence auch leicht selbst implementieren.

    PS: Explizite instantiierungen gehen auch direkter:

    template struct test<5>;
    


  • Arcoth schrieb:

    Huch, da fällt mir was viel hübscheres ein:

    In der Tat, index_sequence kannte ich noch garnicht, bzw. habe es selbst gebastelt.



  • Hm, zu früh gefreut, es kompiliert nicht:

    #include <array>
    #include <atomic>
    #include <utility>
    
    template <std::size_t N, typename = std::make_index_sequence<N>>
    struct test {};
    
    template <std::size_t N, std::size_t... indices>
    struct test<N, std::index_sequence<indices...>> {
    	std::array<int, N> arr{ };
    	std::array<std::atomic<int*>, N> a{ { &arr[indices] }... };
    };
    
    int main() {
    	test<5> t;
    }
    

    Jedenfalls beschweren sich Clang und GCC, VS nimmt es, aber sicher nicht standardkonform. Ich muss also trotzdem mein Array von oben benutzen, also:

    #include <array>
    #include <atomic>
    #include <utility>
    
    template <std::size_t N, typename = std::make_index_sequence<N>>
    struct test {};
    
    template <typename T, std::size_t N>
    struct atomic_array : public std::array<std::atomic<T>, N> {
    	atomic_array(std::initializer_list<T> list) {
    		auto iter = std::begin(*this);
    		for(auto& elem : list)
    			*iter++ = std::move(elem);
    	}
    };
    
    template <std::size_t N, std::size_t... indices>
    struct test<N, std::index_sequence<indices...>> {
    	std::array<int, N> arr{ };
    	atomic_array<int*, N> a{ { &arr[indices] }... };
    };
    
    int main() {
    	test<5> t;
    }
    

    So geht es (endlich)!


  • Mod

    Edit: Alles Quatsch. Habe lediglich einen Flüchtigkeitsfehler eingebaut 🤡
    So funktionierts:

    std::array<std::atomic<int*>, N> a {{ { &arr[indices] }... }};
    


  • Warum kann ich std::array<std::atomic<int*>, N> mit einer std::initializer_list<std::initializer_list<int*>> initialisieren?


  • Mod

    Jodocus schrieb:

    Warum kann ich std::array<std::atomic<int*>, N> mit einer std::initializer_list<std::initializer_list<int*>> initialisieren?

    Weil kein entsprechender Konstruktor vorhanden ist? Du kannst lediglich Klassen mit einem initializer_list<> Objekt initialisieren wenn ein entsprechender Konstruktor vorhanden ist. Aggregate haben keine vom User bereitgestellten Konstruktoren. Was in meinem (jetzt endlich) korrigierten Code verwendet wird, verwendet initializer_list gar nicht.



  • Ich hab gefragt, warum ich es kann, da ich deinen Code so interpretiere (und eben array keinen solchen Konstruktor hat). Und da sind keine Initializer_lists drin? Was ist dann der Unterschied zwischen

    std::array<std::atomic<int*>, N> a{ &arr[indices]... };
    
    std::array<std::atomic<int*>, N> a{{ &arr[indices]... }};
    

    und

    std::array<std::atomic<int*>, N> a{{ { &arr[indices] }... }};
    

    ?
    Das erste ist die normale Aggregat-Initialisierung. Das zweite ist eine Aggregat-Initialisierung eines temporären Arrays, das dann kopiert wird? Und das dritte?


  • Mod

    std::array<std::atomic<int*>, N> a{ &arr[indices]... };
    

    initialisiert jedes Element e n via

    std::atomic<int*> e_n = &arr[n];
    

    Diese Form der initialisierung von a ist tatsächlich equivalent zu1

    std::array<std::atomic<int*>, N> a{{ &arr[indices]... }};
    

    Jedoch können die Klammern in diesem Fall weggelassen werden. In keinem der beiden Fälle wird ein temporäres Array oder array erstellt. Es werden lediglich Temporaries durch copy-initialization benötigt. Dir ist sicherlich bekannt, dass auch in

    std::string s = "abc";
    

    eine Temporary erstellt wird?

    Der Große Unterschied der nun in der dritten Variante ausgenutzt wird, ist dass

    std::array<std::atomic<int*>, N> a{{ { &arr[indices] }... }};
    

    Eine Initialisierung a la

    std::atomic<int*> e_n = {&arr[n]};
    

    impliziert - aber hier brauchen wir - dank der Regeln von list-initialization - keine Temporary, denn der Konstruktor von e n wird direkt mit &arr[n] aufgerufen.

    Wenn wir jedoch

    std::array<std::atomic<int*>, N> a{ { &arr[indices] }... };
    

    schreiben, dann wird zuallererst versucht, dass interne Array mit { &arr[0] } zu initialisieren, was natürlich fehlschlägt.
    ---
    1 Ich bin mir gerade nicht einmal sicher, ob die erste Variante standardkonform ist, da §8.5.1/11 was anderes suggeriert, aber da alle mir bekannten Implementierungen es per se annehmen, scheine ich da etwas falsch zu interpretieren



  • Arcoth schrieb:

    Eine Initialisierung a la

    std::atomic<int*> e_n = {&arr[n]};
    

    impliziert - aber hier brauchen wir - dank der Regeln von list-initialization - keine Temporary, denn der Konstruktor von e n wird direkt mit &arr[n] aufgerufen.

    Meinst du nicht eher, dass das

    std::atomic<int*> e_n{&arr[n]}; // ohne =
    

    impliziert? Gemäß http://en.cppreference.com/w/cpp/language/list_initialization wäre das mit dem = eine copy-list initialization und nicht direct.


  • Mod

    Beide. In jeder Form von list-initialization werden die initializer-clauses stets direkt an den Konstruktor weitergeleitet - außer ein initializer_list Konstruktor wird aufgerufen, was hier aber offensichtlich nicht der Fall ist.

    Edit: Was genau meinste gerade? Bin etwas verwirrt. Dass die Arrayelemente bei Aggregatinitialisierung stets mittels copy-initialization initialisiert werden, habe ich bereits aufgezeigt.



  • Arcoth schrieb:

    Bin etwas verwirrt.

    Hat sich erledigt, ich hab mir die Regeln im Standard noch mal durchgelesen. Danke!


  • Mod

    Hab' auch mein vorhin erwähntes Problem gelöst:

    1 Ich bin mir gerade nicht einmal sicher, ob die erste Variante standardkonform ist, da §8.5.1/11 was anderes suggeriert, aber da alle mir bekannten Implementierungen es per se annehmen, scheine ich da etwas falsch zu interpretieren

    Dieser Paragraph wurde durch CWG #1270 angepasst, sodass

    std::array<int, 1> arr{0};
    

    seit C++14 gültig ist.



  • Arcoth schrieb:

    Edit: Was genau meinste gerade? Bin etwas verwirrt. Dass die Arrayelemente bei Aggregatinitialisierung stets mittels copy-initialization initialisiert werden, habe ich bereits aufgezeigt.

    Was ich meinte, ist, dass eine List-Initialization à la

    std::atomic<int*> e_n = {&arr[n]};
    

    Auch ein Temporary erzeugt, im Gegensatz zu

    std::atomic<int*> e_n{&arr[n]};
    

    , wobei letzteres wohl bei

    std::array<std::atomic<int*>, N> a{{ { &arr[indices] }... }};
    

    passiert.


Anmelden zum Antworten