10

Questo funziona:Perché è possibile inizializzare una serie regolare da {}, ma non uno std :: array di

int arr[10] = {}; 

Tutti gli elementi di arr sono il valore-inizializzato a zero.

Perché non questo lavoro:

std::array<int, 10> arr({}); 

ottengo il seguente avvertimento da g ++ (versione 4.8.2):

avvertimento: inizializzatore per il membro std' mancante :: serie < int, 10ul> :: _ M_elems '

+0

_ "questo non funziona ... ricevo il seguente avviso" _ Quindi ha funzionato allora. Se non funzionasse, non sarebbe stato compilato! –

+0

@JonathanWakely Credo che l'OP cercasse di esprimere sorpresa con l'avvertimento e l'ho trovato sorprendente. Questo è davvero problematico se stai usando '-Werror', che faccio io.È bello vedere che l'avvertenza è stata rimossa dalle versioni recenti ma che non aiuta chi non può effettuare l'aggiornamento :-( –

+0

@ShafikYaghmour, ma l'OP non stava usando -Werror (o non direbbe "avviso") e l'uso indiscriminato di -Werror senza un uso giudizioso di -Wno-xxxx non è necessariamente una buona idea.Vedo che l'avviso è sorprendente, ** ma funziona **, tutti gli elementi sono inizializzati a zero come previsto. come sottolinea AnT, usare '({})' è strano e dovrebbe essere scoraggiato, i bambini punteranno e ridono di te per strada se lo fai. –

risposta

12

Ci sono due problemi uno che è una questione di stile e l'avviso.

Sebbene possa non essere ovvio, l'inizializzazione di aggregazione avviene su un temporaneo che viene quindi utilizzato come argomento per il costruttore di copie. Il più idiomatico per fare questa inizializzazione sarebbe il seguente:

std::array<int, 10> arr = {}; 

Anche se questo lascia ancora l'avviso.

L'avvertimento è coperto da gcc bug report: - -Wmissing-field-initializers relaxation request e uno dei commenti dice:

[...] Sicuramente, la sintassi C++ di dire MyType x = {}; dovrebbe essere sostenuta, come si vede qui:

http://en.cppreference.com/w/cpp/language/aggregate_initialization

dove per esempio:

struct S { 
    int a; 
    float b; 
    std::string str; 
}; 

S s = {}; // identical to S s = {0, 0.0, std::string}; 

che non dovrebbe mettere in guardia per i motivi indicati nei commenti precedenti.

e un commento di follow-up dice:

La mia dichiarazione circa zero-inizializzazione è stato impreciso (grazie), ma il punto generale si trova ancora: in C devi scrivere '= {0 } 'dal momento che inizializzatore di parentesi vuote non è supportato dalla lingua (si ottiene un avviso con -pedantic); in C++, puoi scrivere '= {}' o 'T foo = T();', ma non è necessario scrivere '= {0}' in modo specifico.

Le ultime versioni di gcc non producono questo avviso per questo caso, see it live working with gcc 5.1.

Possiamo vedere questo argomento anche nelle liste degli sviluppatori Clang nel numero: -Wmissing-field-initializers.

Per riferimento il progetto standard C++ 11 sezione 8.5.1[dcl.init.aggr] dice:

Se ci sono meno di inizializzazione-clausole della lista che ci sono membri nel complesso, quindi ogni membro non esplicitamente inizializzato va inizializzato da una lista vuota di inizializzazione (8.5.4) . [ Esempio:

struct S { int a; const char* b; int c; }; 
S ss = { 1, "asdf" }; 

inizializza ss.a con 1, ss.b con "asdf", e ss.c con il valore di un'espressione della forma int(), cioè, 0. - fine esempio]

Così come questo è C++ valido, anche se come indicato utilizzando {} non è valido C99. Si potrebbe obiettare che si tratta solo di un avvertimento, ma questo sembra C++ idiomatico usando {} per l'inizializzazione degli aggregati ed è problematico se si utilizza -Werror per trasformare gli avvisi in errori.

+1

Mi sono appena imbattuto in questo problema nel mio codice stava facendo la ricerca un po 'di tempo fa. –

+0

A giudicare dai tuoi commenti su questa domanda, questo problema ti causa un po 'di angoscia. La mia risposta qui di seguito fornisce una soluzione semplice. –

5

primo luogo, è può utilizzare il ({}) inizializzatore con un oggetto std::array, ma semanticamente che sta per inizializzazione diretta usando costruttore di copia da un std::array oggetto valore inizializzato temporaneo, cioè è equivalente a

std::array<int, 10> arr(std::array<int, 10>{}); 

E in realtà dovrebbe compilare.

In secondo luogo, non si ha realmente bisogno di andare il modo ({}) quando si può semplicemente fare

std::array<int, 10> arr = {}; 

o

std::array<int, 10> arr{}; 

Il primo dei due è il più sintatticamente simile al vostro int arr[10] = {}; , il che mi fa chiedere perché non l'abbia provato all'inizio. Perché hai deciso di utilizzare ({}) anziché = {} quando hai creato la versione della sintassi = {}?

+0

Nota, si compila, è solo un avvertimento, che è un problema se si usa '-Werror'. Nessuna delle alternative suggerite rimuove l'avviso, [vedi live] (http://melpon.org/wandbox/permlink/w3UVJCmv1hdYbsZU). Alla fine della giornata si tratta ancora di aggregare l'inizializzazione e un avviso non necessario per quello che dovrebbe essere considerato un modo idiomatico per inizializzare un aggregato. –

+0

Perché ho pensato (erroneamente) che questo avrebbe chiamato il costruttore predefinito. Vedi questo post: http://stackoverflow.com/questions/31278377/do-empty-braces-call-the-default-constructor-or-the-constructor-taking-an-stdi – AlwaysLearning

+0

@ShafikYaghmour lo standard C++ non distinguere tra "avviso" e "errore". I compilatori comuni sono incoerenti anche a questo riguardo; ad esempio "warning" è dato da gcc per alcune violazioni dei vincoli e per alcuni codici corretti. Quindi non è corretto per qualcuno chiudere un messaggio perché è "solo un avvertimento"; il problema deve essere compreso per primo. –

2

numero sufficiente di persone hanno fatto notare come un "problema" quando si compila con -Werror che penso vale la pena ricordare che il problema va via se basta raddoppiare:

std::array<int, 10> arr{{}}; 

non produce alcun avvertimento per me su gcc 4.9.2.

Per aggiungere un po 'al motivo per cui questo lo risolve: ho capito che std :: array è in realtà una classe con un array C come unico membro. Quindi raddoppiare le parentesi ha senso: le parentesi esterne indicano che si sta inizializzando la classe, e quindi le parentesi interne predefinite inizializzano l'unico membro della classe.

Poiché non vi è alcuna ambiguità quando c'è una sola variabile in una classe, usare solo una coppia di {} dovrebbe essere ragionevole, ma gcc è eccessivamente pedante qui, e avverte.

Problemi correlati