Ten kod:
#include <iostream>
#include <string>
std::pair<std::initializer_list<std::string>, int> groups{ { "A", "B" }, 0 };
int main()
{
for (const auto& i : groups.first)
{
std::cout << i << '\n';
}
return 0;
}
kompiluje, ale zwraca segfault. Dlaczego?
Testowany na gcc 8.3.0 i na kompilatorach online.
std::pair
.Odpowiedzi:
std::initializer_list
nie jest przeznaczony do przechowywania, jest po prostu przeznaczony ... do inicjalizacji. Wewnętrznie przechowuje wskaźnik do pierwszego elementu i rozmiaru. W twoim kodziestd::string
obiekty są tymczasowe iinitializer_list
żadne z nich nie bierze na siebie własności, ani nie przedłuża ich życia, ani nie kopiuje (ponieważ nie jest to kontener), więc wychodzą poza zakres natychmiast po utworzeniu, aleinitializer_list
nadal masz do nich wskaźnik. Dlatego pojawia się błąd segmentacji.Do przechowywania należy użyć pojemnika, takiego jak
std::vector
lubstd::array
.źródło
initializer_list
. Nie można używać obiektów tylko do ruchu, więc nie można na przykład użyć init list z wektorem Unique_ptr. Rozmiarinitializer_list
nie jest stałą czasową kompilacji. I fakt, żestd::vector<int>(3)
istd::vector<int>{3}
robić zupełnie inne rzeczy.Chciałbym tylko dodać trochę więcej szczegółów. Podstawowa tablica
std::initializer_list
zachowuje się podobnie jak tymczasowe. Rozważ następującą klasę:i jego użycie w następującym kodzie:
Drukuje
ponieważ w pierwszym wierszu
X
tworzona jest tymczasowa instancja typu (poprzez konwersję konstruktora z1
), a także niszczona. Zapisane w nim odwołaniep
zwisa.Jeśli zaś
std::initializer_list
używasz go w ten sposób:wtedy podstawowa (tymczasowa) tablica istnieje tak długo, jak
l
wyjścia. Dlatego wynik jest następujący:Jeśli jednak przełączysz się na
Wyjście jest znowu
ponieważ podstawowa (tymczasowa) tablica istnieje tylko w pierwszym wierszu. Odsunięcie wskaźnika do elementów,
l
a następnie powoduje niezdefiniowane zachowanie.Demo na żywo jest tutaj .
źródło