2015-02-27 21 views
10

Rozważmy tę próbkę kodu:Wracając std :: initializer_list w brzękiem

#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; 
} 

Próbowałem skompilować go z g ++ (gcc w wersji 4.9.2 (Debian 4.9.2-10)) i wyjście poprawny. W brzękiem ++ (Debian dzyń wersja 3.5.0-9 (tags/RELEASE_350/final) (oparty na LLVM 3.5.0)) wyjście na przykład:

0 
2125673120 
32546 

Jeżeli pierwsza linia jest zawsze 0, a dwa ostatnie są "losowy".

To błąd w klangu lub coś innego? Myślę, że ta próbka kodu jest poprawna.

Aktualizacja:

Gdy typ lambda powrót funkcja jest coś innego (np std :: vector lub std :: array) ten kod działa poprawnie.

+0

akceptując powielone zalecenie: kod jest funkcjonalnie identyczny jak w przykładzie na dole połączonego pytania (pętla oparta na zasięgu dla 'auto && __begin =') –

Odpowiedz

4

z C++ 11 8.5.4 Lista inicjalizacji [dcl.init.list]

5 obiektu typu std::initializer_list<E> jest wykonana z listy inicjatora jak implementacja przydzielona tablicę N elementami typu E, gdzie N jest liczbą elementów na liście inicjalizatora. Każdy element tej tablicy jest inicjowany przy użyciu odpowiedniego elementu listy inicjalizacyjnej, a obiekt std::initializer_list<E> jest skonstruowany w celu odniesienia do tej tablicy. Jeśli wymagana jest zwężająca się konwersja w celu zainicjowania dowolnego elementu, program jest źle sformułowany.

6 Żywotność tablicy jest taka sama jak obiektu initializer_list.

Oświadczenie Twojej lambda o return inicjuje tymczasowe std::initializer_list<int> i zwraca jego kopię. Wszystko to jest dobre, z wyjątkiem tego, że czas życia tablicy, do której się odnosi, kończy się na końcu pełnego wyrażenia. Dostęp do tablicy martwej przez initializer_list poza lambdą powoduje niezdefiniowane zachowanie.

An initializer_list nie jest kontenerem, jest odniesieniem do tymczasowego kontenera. Jeśli spróbujesz użyć go jak kontenera, będziesz miał zły czas.

W C++ 14 (podając N4140) paragraf 6 wyjaśniono do:

tablicy 6 ma taką samą trwałość, jak każdy inny czasowy przedmiotu (12.2), z wyjątkiem, że inicjowanie obiektu initializer_list z tablicy przedłuża żywotność macierzy dokładnie tak, jak wiąże się z odniesieniem do tymczasowego.

w rozdzielczości CWG issue 1290. To wyjaśnienie uniemożliwia użycie initializer_list jako np. Zmiennej składowej, która była intencją C++ 11. Nawet w C++ 14 Twój program ma niezdefiniowane zachowanie.

+0

... ale specyfikacja pętli zależnych od zakresu definiuje obiekt 'initializer_list', który ma zostać przypisany do zmiennej z (wydedukowanym) typem wartości rvalue:' auto && __range = range-init; 'i tym samym wydłużono okres istnienia tymczasników , prawda? Nie zwraca kopii pliku tymczasowego, zwracany jest sam tymczasowy: jest to tymczasowa wartość zwracana tej wartości lambda, do której odniesienie jest przypisane. Czy może czegoś brakuje? – Columbo

+0

@Columbo Podstawowa tablica powinna zostać zniszczona zanim lambda powróci. –

1

W C++ 11, podstawowa tablica nie ma gwarancji, że istnieje po zakończeniu okresu istnienia pierwotnego obiektu listy inicjalizacyjnej. Dlatego Twój kod może wykazywać niezdefiniowane zachowanie. Przejdź na C++ 14.

+0

Próbowałem skompilować z flagami: -std = C++ 11 i -std = C++ 14. To samo zachowanie. –

+0

@ MichałSzczepaniak to prawdopodobnie używasz starej standardowej implementacji biblioteki (prawdopodobnie od kiedy jesteś na Debianie). – rightfold

+0

Czy możesz podać tekst referencyjny na poparcie tego? – chris

Powiązane problemy