2015-02-27 19 views
10

Considerate questo esempio di codice:Tornando std :: initializer_list in clang

#include <initializer_list> 
#include <iostream> 

int main() 
{ 
    for(auto e: []()->std::initializer_list<int>{return{1,2,3};}()) 
     std::cout<<e<<std::endl; 
    return 0; 
} 

ho provato a compilare con g ++ (gcc versione 4.9.2 (Debian 4.9.2-10)) e l'uscita è corretta. In clang ++ (Debian clang versione 3.5.0-9 (tag/RELEASE_350/def) (sulla base di LLVM 3.5.0)) Uscita per esempio:

0 
2125673120 
32546 

Dove prima linea sono sempre 0 e l'ultimo due sono "random".

È errore in clang o qualcos'altro? Penso che questo esempio di codice sia corretto.

Aggiornamento:

Quando il tipo di funzione lambda di ritorno è un'altra cosa (per esempio std :: vector o std :: array) questo codice funziona bene.

+0

accettando la raccomandazione duplicata: il codice qui è funzionalmente identico all'esempio nella parte inferiore della domanda collegata (basata su intervallo per i collegamenti loop come 'auto && __begin =') –

risposta

4

Da C++ 11 8.5.4 Lista inizializzazione [dcl.init.list]:

5 Un oggetto di tipo std::initializer_list<E> è costruito da una lista di inizializzazione come se l'attuazione allocato un array di elementi N di tipo E, dove N è il numero di elementi nell'elenco di inizializzazione. Ogni elemento di quell'array è inizializzato in copia con l'elemento corrispondente dell'elenco di inizializzazione e l'oggetto std::initializer_list<E> viene costruito per fare riferimento a tale array. Se è necessaria una conversione restringente per inizializzare uno qualsiasi degli elementi, il programma è mal formato.

6 La durata della serie è uguale a quella dell'oggetto initializer_list.

L'istruzione return di lambda inizializza uno std::initializer_list<int> temporaneo e ne restituisce una copia. Tutto ciò va bene, tranne per il fatto che la durata dell'array a cui si riferisce termina alla fine dell'espressione completa. L'accesso alla matrice morta tramite il numero initializer_list al di fuori della lambda provoca un comportamento non definito.

Un initializer_list non è un contenitore, è un riferimento a un contenitore temporaneo. Se provi ad usarlo come un contenitore, avrai un brutto momento.

In C++ 14 (citando N4140) il paragrafo 6 è stato chiarito a:

6 La matrice ha la stessa durata come qualsiasi altro oggetto temporaneo (12.2), tranne che inizializzando un oggetto initializer_list dalla matrice estende la durata della matrice esattamente come un riferimento a un riferimento temporaneo.

dalla risoluzione di CWG issue 1290. Questo chiarimento rende impossibile l'uso di uno initializer_list come, ad esempio, una variabile membro che era l'intenzione di C++ 11. Anche in C++ 14, tuttavia, il tuo programma ha un comportamento indefinito.

+0

... ma la specifica dei loop basati su range definisce l'oggetto 'initializer_list' da assegnare a una variabile con (dedotto) il tipo di riferimento rvalue:' auto && __range = range-init; 'e la durata dei temporaries è quindi estesa non è vero? Nessuna copia del temporaneo viene restituita, viene restituito il temporaneo stesso: è il valore di ritorno temporaneo di quella lambda a cui è associato il riferimento. O mi sta sfuggendo qualcosa? – Columbo

+0

@Columbo L'array sottostante deve essere distrutto prima che il lambda ritorni. –

1

In C++ 11, non è garantito che l'array sottostante esista al termine della durata dell'oggetto della lista inizializzatore originale. Pertanto, il codice potrebbe presentare un comportamento indefinito. Passare a C++ 14.

+0

Ho provato a compilare con flag: -std = C++ 11 e -std = C++ 14. Lo stesso comportamento. –

+0

@ MichałSzczepaniak quindi probabilmente stai usando una vecchia implementazione di libreria standard (che è probabile dato che sei su Debian). – rightfold

+0

Potresti fornire del testo di riferimento per supportarlo? – chris

Problemi correlati