2016-12-02 7 views
1

Предположим, у меня есть std::vector размера, известного во время компиляции, и я хочу превратить его в std::array. Как мне это сделать? Есть ли стандартная функция для этого?Использовать std :: vector для std :: array initialization

Лучшее решение, которое я до сих пор это:

template<class T, std::size_t N, class Indexable, std::size_t... Indices> 
std::array<T, N> to_array_1(const Indexable& indexable, std::index_sequence<Indices...>) { 
    return {{ indexable[Indices]... }}; 
} 

template<class T, std::size_t N, class Indexable> 
std::array<T, N> to_array(const Indexable& indexable) { 
    return to_array_1<T, N>(indexable, std::make_index_sequence<N>()); 
} 

std::array<Foo, 123> initFoos { 
    std::vector<Foo> lst; 
    for (unsigned i = 0; i < 123; ++i) 
    lst.push_back(getNextFoo(lst)); 
    return to_array<Foo, 123>(lst); // passing lst.begin() works, too 
} 

Применение аналогично Populate std::array with non-default-constructible type (no variadic templates): У меня тоже есть тип, который не по-умолчанию, конструктивны, так что мне нужно, чтобы вычислить фактические значения К время, когда массив инициализируется. Однако, вопреки этому вопросу, для меня значения являются не просто функцией индекса, но и предыдущих значений. Я могу строить свои ценности гораздо проще с помощью цикла, чем с помощью ряда вызовов функций. Поэтому я строю элементы в цикле и помещаю их в вектор, тогда я хочу использовать конечное состояние этого вектора для инициализации массива.

Выше, кажется, компилируется и работает нормально, но, возможно, есть способы его улучшить.

  1. Возможно, я мог бы умело использовать некоторые стандартные функции библиотеки, о которых я не знал.
  2. Возможно, я могу как-то избежать вспомогательной функции.
  3. Возможно, я могу как-то сформулировать это так, чтобы он работал с семантикой перемещения для элементов вместо семантики копирования, используемой выше.
  4. Возможно, я могу избежать случайного доступа, используя operator[], и вместо этого использовать семантику прямого итератора только для того, чтобы она работала для std::set или std::forward_list в качестве ввода.
  5. Возможно, мне следует прекратить использовать std::vector и вместо этого выразить свою цель, используя вместо этого std::array<std::optional<T>, N>, используя C++ 17 или некоторую эквивалентную реализацию.

Похожие вопросы:

  • Copy std::vector into std::array который не принимает тип без конструктора по умолчанию, поэтому копирование после инициализации по умолчанию, это возможно там, но не для меня.
  • Populate std::array with non-default-constructible type (no variadic templates), который вычисляет каждый элемент из своего индекса независимо, поэтому он пытается избежать промежуточного контейнера. Ответы используют вариативные шаблоны, даже если заголовок требует их избегать.
+1

Интересная проблема, но это плохо пахнет. Зачем нужно генерировать данные только для их копирования? Это заставляет ваше желание упаковать их в 'std :: array', но в чем преимущество этого по сравнению с исходным' std :: vector'? Оба сохраняют данные в последовательной части памяти, поэтому не должно быть преимуществ первого по сравнению с последним. Единственное различие заключается в том, что 'size' является переменной времени компиляции, но это так или иначе. – Walter

+0

@Walter: Действительный вопрос. С одной стороны, мой оригинал является «std :: set», поэтому мне нужно преобразовать хотя бы один раз. И я использую конструктор преобразования для выполнения преобразования типов для элементов. Таким образом, дело не только в том, чтобы хранить 'std :: vector'. Я мог бы использовать его. Главным аргументом против, наверное, является мое чувство, что чем больше компилятор знает, тем лучше он может оптимизировать. На практике я имею дело только с 24 элементами, и хотя это может быть слишком много для полной развертки цикла, это может быть, например, разворачивают группы из четырех человек, зная, что счет делится на 4. Просто чувство. – MvG

ответ

3

Я хотел бы предложить:

template<typename T, typename Iter, std::size_t... Indices> 
std::array<T, sizeof...(Indices)> to_array(Iter iter, std::index_sequence<Indices...>) { 
    return {{((void)Indices, *iter++)...}}; 
} 

template<typename T, std::size_t N, typename Iter> 
std::array<T, N> to_array(Iter iter) { 
    return to_array<T>(std::move(iter), std::make_index_sequence<N>{}); 
} 

Это оставляет копирования против передвигающейся семантики до вызывающей –, если вызывающий абонент хочет двигаться, они могут отказаться в через std::move_iterator или тому подобный:

auto initFoos() { 
    std::vector<Foo> lst; 
    for (unsigned i{}; i != 123u; ++i) { 
     lst.push_back(getNextFoo(lst)); 
    } 
    // copy-init array elements: 
    return to_array<Foo, 123>(lst.cbegin()); 
    // or move-init array elements: 
    return to_array<Foo, 123>(std::make_move_iterator(lst.begin())); 
} 

Online Demo

+0

Каков порядок оценки нескольких выражений 'iter ++ '? –

+2

@MaximEgorushkin: Все расширения внутри списка элементов привязки гарантированно выполняются слева направо. – ildjarn

+1

Спасибо! Это касается пунктов 3 и 4 моего списка, и я не знал об итераторе перемещения. В моем фактическом приложении я использую явный конструктор преобразования, инициализируя 'array ' из 'vector ' используя 'Bar :: Bar (const Foo &)' и, возможно, 'Bar :: Bar (Foo &&)' в будущем. Оказывается, код, который вы дали, не считается явным преобразованием, а мой оригинальный. Поэтому я добавил там вызов конструктора 'T (...)'. – MvG