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

Преобразовать вектор <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.

4b9b3361

Ответ 1

Я опубликовал способ, который, казалось, работал, но, к сожалению, вызвал нарушения доступа к памяти из-за того, как 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;
}

Ответ 2

Ответ НЕТ, вы не можете этого сделать.

Объектом типа std::initializer_list<T> является легкий прокси-объект, который обеспечивает доступ к массиву объектов типа Т. Объект std::initializer_list автоматически построен, когда:

  • в инициализации списка используется список бит-init-init, в том числе инициализация списка функций и присваивание (не путать с списками инициализаторов конструктора)
  • бит-init-list привязан к авто, в том числе в цикле для диапазона

Что касается поддержки библиотеки, std::initializer_list имеет только конструктор по умолчанию, который создает пустой список, а его итераторы постоянны. Отсутствие члена push_back() означает, что вы не можете применить, например. a 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
}

Пример Live

Если вы посмотрите на стандартные контейнеры, в дополнение к принятию std::initializer_list в своих конструкторах/вставках, все они имеют конструкторы/вставки, берущие пару итераторов, и реализация, вероятно, делегирует функцию initializer_list соответствующему итератор. Например. функция std::vector<T>::insert в libС++ - это простой однострочный интерфейс:

 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

Ответ 3

По-видимому нет, это невозможно. Нет такого конструктора (и я верю по уважительным причинам), std::initializer_list - странное существо.

Вместо этого вы можете изменить someThing(), чтобы принять пару итераторов. Таким образом вы получаете то, что хотите, при условии, что вы можете изменить подпись этой функции (это не в сторонней библиотеке и т.д.).

Ответ 4

Да, вы можете это сделать, но вы не хотите этого делать, потому что, как вы должны это делать, это довольно глупо.

Сначала определите, какая максимальная длина вашего списка. Должна быть максимальная длина, потому что size_t не является неограниченным. Идеально найти лучший (меньший), например 10.

Во-вторых, напишите волшебный код переключения, который принимает целое число времени выполнения и сопоставляет его с целым числом времени компиляции, а затем вызывает класс или функцию шаблона с таким количеством времени компиляции. Такой код требует максимального целочисленного размера - используйте максимальную длину выше.

Теперь волшебство переключит размер вектора на длину времени компиляции.

Создайте временную последовательность компиляции целых чисел, от 0 до length-1. Распакуйте эту последовательность в конструкции initializer_list, каждый раз вызывая [] на std::vector. Вызовите свою функцию с результатом initializer_list.

Вышеизложенное сложно и смешно, и большинство компиляторов взорвут на него. Есть один шаг, который я не уверен в законности - это построение initializer_list юридического места для разборки varardic аргументов?

Вот пример волшебного переключателя: Могу ли я отделить места создания и использования стратегий времени компиляции?

Вот пример индексов, или последовательность, трюк: Аргументы конструктора из кортежа

Этот пост должен быть только теоретическим, потому что практически это действительно глупый способ решить эту проблему.

Выполнение этого с помощью произвольного итерабельного сложнее, не делая n ^ 2 работы. Но поскольку вышесказанное уже смешно, а произвольная итерируемая версия была бы более смешной... (Может быть, с пакетом лямбда - получение этого для того, чтобы аргументы оценивались по порядку, может быть сложным. Существует ли точка последовательности между оценка различных аргументов в списке инициализаторов?)

Ответ 5

Для вектора вы можете использовать арифметику указателя.

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() ) ) ;

Ответ 6

Если вы не возражаете против копий, то я думаю, что что-то вроде этого будет работать:

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);
}