Подтвердить что ты не робот

Возврат std:: initializer_list в 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;
}

Я попытался скомпилировать его с g++ (gcc версия 4.9.2 (Debian 4.9.2-10)) и выход правильный. В clang++ (Debian clang version 3.5.0-9 (теги/RELEASE_350/final) (на основе LLVM 3.5.0)), например:

0
2125673120
32546

Где первая строка всегда 0, а последние две являются "случайными".

Это ошибка в clang или что-то еще? Я думаю, что этот образец кода верен.

Update:

Когда возвращаемый тип лямбда-функции является чем-то другим (например, std::vector или std:: array), этот код работает нормально.

4b9b3361

Ответ 1

Из С++ 11 8.5.4 Инициализация списка [dcl.init.list]:

5 Объект типа std::initializer_list<E> создается из списка инициализаторов, как если бы реализация выделила массив элементов N типа E, где N - количество элементов в списке инициализаторов. Каждый элемент этого массива инициализируется копией с соответствующим элементом списка инициализаторов, а объект std::initializer_list<E> создается для обращения к этому массиву. Если для инициализации любого из элементов требуется сужение преобразования, программа плохо сформирована.

6 Время жизни массива такое же, как у объекта initializer_list.

Оператор return вашей лямбды инициализирует временный std::initializer_list<int> и возвращает его копию. Это все хорошо, за исключением того, что время жизни массива, к которому он относится, заканчивается в конце полного выражения. Доступ к мертвому массиву через initializer_list вне лямбда приводит к поведению undefined.

An initializer_list не является контейнером, это ссылка на временный контейнер. Если вы попытаетесь использовать его как контейнер, у вас будет плохое время.

В С++ 14 (цитируя N4140) пункт 6 разъяснен следующим образом:

6 Массив имеет такое же время жизни, что и любой другой временный объект (12.2), за исключением того, что инициализация объекта initializer_list из массива продлевает время жизни массива точно так же, как привязка ссылки к временному.

по разрешению CWG issue 1290. Это разъяснение делает невозможным использование initializer_list как, например, переменной-члена, которая была намерением С++ 11. Однако даже в С++ 14 ваша программа имеет поведение undefined.

Ответ 2

В С++ 11 базовый массив не гарантированно существует после того, как закончилось время жизни исходного объекта списка инициализации. Поэтому ваш код может демонстрировать поведение undefined. Переключитесь на С++ 14.