Пакетное расширение списка переменных типов в список инициализаторов сложных типов — это законно?

Я хотел бы «материализовать» список типов переменных в 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";
}
}

Жить на Колиру

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

8

Решение

Две вещи о initializer_list:

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

а также

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

так в этой строке

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

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

9

Другие решения

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

(акцент мой)

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

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

3

По вопросам рекламы [email protected]