Этот код:
#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;
}
компилируется, но возвращает segfault. Почему?
Протестировано на gcc 8.3.0 и на онлайн-компиляторах.
std::pair
.Ответы:
std::initializer_list
не предназначен для хранения, он просто предназначен для ... ну инициализации. Внутренне он просто хранит указатель на первый элемент и размер. В вашем кодеstd::string
объекты являются временными, иinitializer_list
ни один из них не становится владельцем этих прав, ни продлевает их жизнь, ни копирует их (потому что это не контейнер), поэтому они выходят из области видимости сразу после создания, но у васinitializer_list
все еще есть указатель на них. Вот почему вы получаете ошибку сегментации.Для хранения вы должны использовать контейнер, как
std::vector
илиstd::array
.источник
initializer_list
. Невозможно использовать объекты только для перемещения, поэтому вы не можете использовать список init с вектором unique_ptr, например. Размерinitializer_list
не является константой времени компиляции. А то, чтоstd::vector<int>(3)
иstd::vector<int>{3}
делаю совершенно разные вещи. Мне грустно :(Я бы просто добавил немного больше деталей. Базовый массив
std::initializer_list
ведет себя примерно так же, как временные. Рассмотрим следующий класс:и его использование в следующем коде:
Распечатывает
поскольку в первой строке создается временный экземпляр типа
X
(путем преобразования конструктора из1
) и также уничтожается. Ссылка, хранящаяся вp
затем болтается.Что касается
std::initializer_list
, если вы используете это так:затем базовый (временный) массив существует до тех пор, пока не
l
завершится. Поэтому на выходе получается:Однако, если вы переключитесь на
Выход снова
поскольку базовый (временный) массив существует только в первой строке. Разыменование указателя на элементы
l
затем приводит к неопределенному поведению.Живая демоверсия здесь .
источник