Versione corta: una clausola di inizializzazione che inizia con {
interrompe brace-elision. Questo è il caso del primo esempio con {1,2}
, ma non nel terzo né nel quarto che usano A{1,2}
. Brace-elision consuma le successive clausole di inizializzazione N (dove N dipende dall'aggregato da inizializzare), motivo per cui solo la prima clausola di inizializzatore di N non deve iniziare con {
.
In tutte le implementazioni della libreria C++ standard che conosco, std::array
è una struttura che contiene una matrice C-style. Cioè, si ha un aggregato che contiene un sub-aggregati, molto simile
template<typename T, std::size_t N>
struct array
{
T __arr[N]; // don't access this directly!
};
Quando si inizializza un std::array
da un rinforzato-init-list, avrete quindi bisogno di inizializzare i membri della contiene l'array. Pertanto, in tali implementazioni, la forma esplicita è:
std::array<A, 4> x = {{ {1,2}, {3,4}, {5,6}, {7,8} }};
Il più esterne parentesi si riferisce alla std::array
struct; la seconda serie di parentesi si riferisce alla matrice in stile C nidificata.
C++ consente nell'inizializzazione di aggregazione di omettere alcune parentesi durante l'inizializzazione di aggregati nidificati. Ad esempio:
struct outer {
struct inner {
int i;
};
inner x;
};
outer e = { { 42 } }; // explicit braces
outer o = { 42 }; // with brace-elision
Le regole sono le seguenti (utilizzando un progetto post-N4527, che è post-C++ 14, ma C++ 11 conteneva un difetto relativa a questo comunque):
Le parentesi graffe possono essere eliminate in un elenco di inizializzazione come segue. Se il inizializzatore-list inizia con un gancio sinistro, quindi il successivo elenco separato da virgole di inizializzazione -clausole inizializza i membri di un subaggregate; è errato che ci siano più clausole di inizializzazione rispetto ai membri.Se, tuttavia, il inizializzazione-list per un subaggregate non inizia con una doppietta a sinistra, allora solo abbastanza inizializzazione-clausole dalla lista sono presi per inizializzare le membri del subaggregate; le rimanenti clausole di inizializzatore sono sinistra per inizializzare il membro successivo dell'aggregato di cui il sottogruppo corrente è un membro.
Applicando questo al primo std::array
-example:
static std::array<A, 4> x1 =
{
{ 1, 2 },
{ 3, 4 },
{ 5, 6 },
{ 7, 8 }
};
questo viene interpretato come segue:
static std::array<A, 4> x1 =
{ // x1 {
{ // __arr {
1, // __arr[0]
2 // __arr[1]
// __arr[2] = {}
// __arr[3] = {}
} // }
{3,4}, // ??
{5,6}, // ??
...
}; // }
Il primo {
è preso come l'inizializzatore del std::array
struct. Le clausole di inizializzatore {1,2}, {3,4}
vengono quindi prese come inizializzatori dei sottoaggregati di std::array
. Si noti che std::array
ha un solo sottoaggregato __arr
. Poiché il primo inizializzatore clausola{1,2}
inizia con un {
, l'eccezione brace-elisione non si verifica, e il compilatore cerca di inizializzare l'nidificata A __arr[4]
array con {1,2}
. Le restanti clausole di inizializzazione {3,4}, {5,6}
ecc. Non si riferiscono a nessun sub-aggregato di std::array
e pertanto sono illegali.
Nel terzo e quarto esempio, il primo inizializzatore clausola per la subaggregate di std::array
non inizia con un {
, quindi viene applicata l'eccezione sinalefe tutore:
static std::array<A, 4> x4 =
{
A{ 1, 2 }, // does not begin with {
{ 3, 4 },
{ 5, 6 },
{ 7, 8 }
};
Così è interpretato come segue:
static std::array<A, 4> x4 =
{ // x4 {
// __arr { -- brace elided
A{ 1, 2 }, // __arr[0]
{ 3, 4 }, // __arr[1]
{ 5, 6 }, // __arr[2]
{ 7, 8 } // __arr[3]
// } -- brace elided
}; // }
Quindi, le cause A{1,2}
tutti e quattro clausole di inizializzazione da consumare per inizializzare la matrice in stile C nidificata. Se si aggiunge un altro inizializzatore:
static std::array<A, 4> x4 =
{
A{ 1, 2 }, // does not begin with {
{ 3, 4 },
{ 5, 6 },
{ 7, 8 },
X
};
allora questo X
sarebbero stati utilizzati per inizializzare la prossima subaggregate di std::array
. Per esempio.
struct outer {
struct inner {
int a;
int b;
};
inner i;
int c;
};
outer o =
{ // o {
// i {
1, // a
2, // b
// }
3 // c
}; // }
Brace-elisione consuma prossimi N-inizializzazione clausole, dove N è definito tramite il numero di inizializzatori necessari per la (sub) aggregato da inizializzare. Pertanto, importa solo se la prima di quelle clausole di inizializzazione N inizia con uno {
.
Più simile al PO:
struct inner {
int a;
int b;
};
struct outer {
struct middle {
inner i;
};
middle m;
int c;
};
outer o =
{ // o {
// m {
inner{1,2}, // i
// }
3 // c
}; // }
noti che brace-elisione applica ricorsivamente; possiamo anche scrivere la confusione
outer o =
{ // o {
// m {
// i {
1, // a
2, // b
// }
// }
3 // c
}; // }
Dove si omettono sia le parentesi per o.m
e o.m.i
. Le prime due clausole di inizializzatore vengono utilizzate per inizializzare lo o.m.i
, il rimanente inizializza o.c
. Una volta inseriamo una coppia di bretelle intorno 1,2
, viene interpretato come la coppia di bretelle corrispondenti a o.m
:
outer o =
{ // o {
{ // m {
// i {
1, // a
2, // b
// }
} // }
3 // c
}; // }
Qui, l'inizializzatore per o.m
si avvia con una {
, quindi brace-elisione non si applica. L'inizializzatore per o.m.i
è 1
, che non inizia con un {
, quindi viene applicato il blocco-elisione per o.m.i
ei due iniziatori 1
e 2
vengono consumati.
Mi dispiace ma non riesco a capire perché il primo compito non riesce a compilare, potresti dirmi di più, per favore? È interessante ! – Telokis
@Ninetainedo - vedere la domanda collegata. – Jeremy
@ dyp - Corretto. – Jeremy