Преобразовать вектор<T> в initializer_list<T>
Каждый создает std::vector
от std::initializer_list
а как же наоборот?
например. если вы используете std::initializer_list
в качестве параметра:
void someThing(std::initializer_list<int> items)
{
...
}
Есть моменты, когда у вас есть свои вещи в vector<T>
вместо буквального списка:
std::vector<int> v;
// populate v with values
someThing(v); // boom! No viable conversion etc.
Более общий вопрос: как создать stl::initializer_list
из STL повторяемого, а не только std::vector
,
8 ответов
Я опубликовал способ, который, казалось, работал, но, к сожалению, вызвал нарушения доступа к памяти из-за того, что initializer_lists обрабатываются как ссылки на локальные копии значений.
Вот альтернатива. Отдельная функция и отдельный статический список инициализатора генерируются для каждого возможного количества элементов, которые учитываются с помощью пакета параметров. Это не потокобезопасно и использует const_cast (который считается очень плохим) для записи в статическую память initializer_list. Тем не менее, он работает чисто как в gcc, так и в clang.
Если по какой-то непонятной причине вам нужно решить эту проблему и у вас нет других вариантов, вы можете попробовать этот метод взлома.
#include <initializer_list>
#include <iostream>
#include <stdexcept>
#include <type_traits>
#include <vector>
namespace __range_to_initializer_list {
constexpr size_t DEFAULT_MAX_LENGTH = 128;
template <typename V> struct backingValue { static V value; };
template <typename V> V backingValue<V>::value;
template <typename V, typename... Vcount> struct backingList { static std::initializer_list<V> list; };
template <typename V, typename... Vcount>
std::initializer_list<V> backingList<V, Vcount...>::list = {(Vcount)backingValue<V>::value...};
template <size_t maxLength, typename It, typename V = typename It::value_type, typename... Vcount>
static typename std::enable_if< sizeof...(Vcount) >= maxLength,
std::initializer_list<V> >::type generate_n(It begin, It end, It current)
{
throw std::length_error("More than maxLength elements in range.");
}
template <size_t maxLength = DEFAULT_MAX_LENGTH, typename It, typename V = typename It::value_type, typename... Vcount>
static typename std::enable_if< sizeof...(Vcount) < maxLength,
std::initializer_list<V> >::type generate_n(It begin, It end, It current)
{
if (current != end)
return generate_n<maxLength, It, V, V, Vcount...>(begin, end, ++current);
current = begin;
for (auto it = backingList<V,Vcount...>::list.begin();
it != backingList<V,Vcount...>::list.end();
++current, ++it)
*const_cast<V*>(&*it) = *current;
return backingList<V,Vcount...>::list;
}
}
template <typename It>
std::initializer_list<typename It::value_type> range_to_initializer_list(It begin, It end)
{
return __range_to_initializer_list::generate_n(begin, end, begin);
}
int main()
{
std::vector<int> vec = {1,2,3,4,5,6,7,8,9,10};
std::initializer_list<int> list = range_to_initializer_list(vec.begin(), vec.end());
for (int i : list)
std::cout << i << std::endl;
return 0;
}
Ответ НЕТ, вы не можете этого сделать.
Объект типа std::initializer_list<T>
это легкий прокси-объект, который обеспечивает доступ к массиву объектов типа T. A std::initializer_list
Объект автоматически создается, когда:
- Braced-init-list используется в инициализации списка, включая инициализацию списка вызовов функций и выражения присваивания (не путать со списками инициализатора конструктора)
- Braced-init-list привязан к auto, в том числе в цикле for
Что касается поддержки библиотеки, std::initializer_list
имеет только конструктор по умолчанию, который создает пустой список, и его итераторы постоянны. Отсутствие push_back()
член означает, что вы не можете подать заявку, например, std::copy
с std::back_inserter
адаптер итератора, чтобы заполнить его, и вы также не можете назначить через такие итераторы напрямую:
#include <algorithm>
#include <initializer_list>
#include <iterator>
#include <vector>
int main()
{
auto v = std::vector<int> { 1, 2 };
std::initializer_list<int> i;
auto it = std::begin(i);
*it = begin(v); // error: read-only variable is not assignable
}
Если вы посмотрите на стандартные контейнеры, в дополнение к принятию std::initializer_list
в своих конструкторах / вставках все они имеют конструкторы / вставщики, принимающие пару итераторов, и реализация, вероятно, делегирует initializer_list
функция к соответствующей парной функции итератора. Например, std::vector<T>::insert
Функция в libC++ - это простая однострочная строка:
iterator insert(const_iterator __position, initializer_list<value_type> __il)
{return insert(__position, __il.begin(), __il.end());}
Вы должны изменить свой код по аналогии:
void someThing(std::initializer_list<int> items)
{
someThing(items.begin(), items.end()); // delegate
}
template<class It>
void someThing(It first, It last)
{
for (auto it = first, it != last; ++it) // do your thing
}
Во времена, когда у вас есть элементы в векторе, а не в буквальном списке:
std::vector<int> v = { 1, 2 };
auto i = { 1, 2 };
someThing(begin(v), end(v)); // OK
someThing(i); // also OK
someThing({1, 2}); // even better
Видимо нет, это невозможно. Там нет такого конструктора (и я думаю, по уважительным причинам), std::initializer_list
странное существо
Вместо этого вы можете изменить someThing()
принять пару итераторов. Таким образом, вы получаете то, что хотите, при условии, что вы можете изменить сигнатуру этой функции (ее нет в сторонней библиотеке и т. Д.).
Да, вы можете сделать это, но вы не хотите делать это, потому что то, как вы должны это делать, довольно глупо.
Сначала определите, какова максимальная длина вашего списка. Должна быть максимальная длина, потому что size_t
не является неограниченным. Идеально найти лучший (меньший), например, 10.
Во-вторых, напишите код магического переключателя, который принимает целое число времени выполнения и отображает его в целое число времени компиляции, а затем вызывает класс или функцию шаблона с этим целым числом времени компиляции. Такой код требует максимального целочисленного размера - используйте максимальную длину выше.
Теперь, магия переключает размер вектора на длительность времени компиляции.
Создать последовательность времени компиляции целых чисел, из 0
в length-1
, Распакуйте эту последовательность в initializer_list
конструкция, каждый раз вызывая []
на std::vector
, Вызовите свою функцию с таким результатом initializer_list
,
Вышеупомянутое сложно и смешно, и большинство компиляторов взорвутся. Есть один шаг, который я не уверен в законности - это строительство initializer_list
законное место, чтобы сделать распаковку varardic аргумента?
Вот пример магического переключения: Могу ли я разделить места создания и использования стратегий времени компиляции?
Вот пример трюка с индексами или последовательностями: аргументы конструктора из кортежа
Этот пост должен представлять только теоретический интерес, потому что практически это действительно глупый способ решить эту проблему.
Делать это с произвольной итерацией сложнее, без выполнения n^2 работы. Но так как вышеупомянутое уже достаточно нелепо, и произвольная итеративная версия была бы более нелепой... (Возможно, с пакетом лямбд - получить его так, чтобы аргументы оценивались по порядку, было бы сложно. Есть ли точка последовательности между оценка различных аргументов в списке инициализаторов?)
Если вы не возражаете против копий, то я думаю, что-то вроде этого будет работать:
template<class Iterator>
using iterator_init_list = std::initializer_list<typename std::iterator_traits<Iterator>::value_type>;
template<class Iterator, class... Ts>
iterator_init_list<Iterator> to_initializer_list(Iterator start, Iterator last, Ts... xs)
{
if (start == last) return iterator_init_list<Iterator>{xs...};
else return to_initializer_list(start+1, last, xs..., *start);
}
Я думаю, что лучшее решение без создания шаблонов неясных классов итераторов для передачи вектора с использованием его двух методов, возвращающих итераторы, - это просто реализовать логику вашей функции в векторной функции.
void someThing(std::initializer_list<int> items)
{
std::vector<int> v;
for(int i:items)
{
v.push_back(i);
}
someThing(v);
}
void someThing(std::vector<int> items)
{
...
}
Для вектора вы можете использовать арифметику указателей.
Foo( std::initializer_list< _Type >( aVector.data(), aVector.data() + aVector.size() ) ) ;
Вы можете обернуть это в универсальную функцию.
template< typename _Type >
auto InitList( const _Type * begin, size_t size )
{
return std::initializer_list< _Type >( begin, begin + size ) ;
}
Вызов функции, как это.
Foo( InitList( aVector.data(), aVector.size() ) ) ;
std::vector<int> v;
someThing(std::initializer_list<int>(&v.front(), &v.front() + v.size()));