Пакетное расширение списка переменных типов в список инициализаторов сложных типов - это законно? - PullRequest
0 голосов
/ 11 сентября 2018

Я хотел бы «материализовать» список типов переменных в initializer_list связанных значений.Например, имея std::tuple из нескольких std::integral_constant<T, x>, вы получите std::initializer_list<T>{...}.В общем случае я хотел бы получить initializer_list некоторого сложного типа, например std::string.

. Но следующий простой пример дает мне сбой при компиляции Clang (хотя он работает с GCC, по крайней мере, на Coliru), поэтому я подозреваю UB (или ошибку в Clang):

template <class... Ts>
std::initializer_list<const std::string> materialize()
{
    return {
      std::to_string(Ts::value)...
    };
}

void print_out()
{
   for (const auto & x : materialize<std::true_type, std::false_type>()) {
      std::cout << x << "\n";
   }
}

Live на Coliru

Итак, такой код законен?В С ++ 11/14/17?

Ответы [ 2 ]

0 голосов
/ 11 сентября 2018

Две вещи в initializer_list:

Списки инициализаторов могут быть реализованы в виде пары указателей или указателя и длины.Копирование списка std :: initializer_list не копирует базовые объекты .

и

Базовый массив не гарантируетсясуществовать после окончания времени жизни исходного объекта списка инициализатора.Хранилище для std :: initializer_list не указано (т. Е. Это может быть автоматическая, временная или статическая память только для чтения, в зависимости от ситуации).

, поэтому в этой строке

return {
      std::to_string(Ts::value)...
    };

вы создаете локальный массив, initializer_list сохраняет указатель на начало / конец этого массива, когда функция выходит из области видимости, у вас есть висячие указатели.

0 голосов
/ 11 сентября 2018

Базовый массив std::initializer_list фактически является локальным временным объектом.Когда выйдешь из materialize он был уничтожен.Копирование std::initializer_list не копирует базовый массив, содержимое возвращенного std::initializer_list всегда недопустимо, и попытка доступа к содержимому возвращенного std::initializer_list приводит к UB.

(выделено мое)

Списки инициализаторов могут быть реализованы в виде пары указателей или указателя и длины. Копирование списка std :: initializer_list не копирует базовые объекты .

Базовый массив представляет собой временный массив типа const T[N], в который копируется каждый элемент-инициализирован (за исключением того, что сужающие преобразования недопустимы) из соответствующего элемента исходного списка инициализатора.Время жизни базового массива такое же, как у любого другого временного объекта, за исключением того, что инициализация объекта initializer_list из массива продлевает время жизни массива точно так же, как привязка ссылки к временному объекту (с теми же исключениями, например, для инициализации нечлен класса).Базовый массив может быть размещен в постоянной памяти.

...