Array-Member initialisieren
-
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 sichmake_index_sequenceauch 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_sequencekannte 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)!
-
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 einerstd::initializer_list<std::initializer_list<int*>>initialisieren?
-
Jodocus schrieb:
Warum kann ich
std::array<std::atomic<int*>, N>mit einerstd::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, verwendetinitializer_listgar 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?
-
std::array<std::atomic<int*>, N> a{ &arr[indices]... };initialisiert jedes Element
en viastd::atomic<int*> e_n = &arr[n];Diese Form der initialisierung von
aist tatsächlich equivalent zu1std::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
arrayerstellt. Es werden lediglich Temporaries durch copy-initialization benötigt. Dir ist sicherlich bekannt, dass auch instd::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
en 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
en 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.
-
Beide. In jeder Form von list-initialization werden die initializer-clauses stets direkt an den Konstruktor weitergeleitet - außer ein
initializer_listKonstruktor 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!
-
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.