Инициализировать std:: array с диапазоном (пара итераторов) - программирование
Подтвердить что ты не робот

Инициализировать std:: array с диапазоном (пара итераторов)

Как я могу инициализировать std::array из диапазона (как определено парой итераторов)?

Что-то вроде этого:

vector<T> v;
...
// I know v has exactly N elements (e.g. I just called v.resize(N))
// Now I want a initialized with those elements
array<T, N> a(???);  // what to put here?

Я думал, что array будет иметь конструктор, беря пару итераторов, так что я мог бы сделать array<T, N> a(v.begin(), v.end()), но он вообще не имеет конструкторов!

Я знаю, что я могу copy использовать вектор в массиве, но я бы предпочел инициализировать массив с векторным содержимым напрямую, не создавая его по умолчанию. Как я могу?

4b9b3361

Ответ 1

При использовании итераторов с произвольным доступом и при определенном размере во время компиляции вы можете использовать индекс индексов для этого:

template <std::size_t... Indices>
struct indices {
    using next = indices<Indices..., sizeof...(Indices)>;
};
template <std::size_t N>
struct build_indices {
    using type = typename build_indices<N-1>::type::next;
};
template <>
struct build_indices<0> {
    using type = indices<>;
};
template <std::size_t N>
using BuildIndices = typename build_indices<N>::type;

template <typename Iterator>
using ValueType = typename std::iterator_traits<Iterator>::value_type;

// internal overload with indices tag
template <std::size_t... I, typename RandomAccessIterator,
          typename Array = std::array<ValueType<RandomAccessIterator>, sizeof...(I)>>
Array make_array(RandomAccessIterator first, indices<I...>) {
    return Array { { first[I]... } };
}

// externally visible interface
template <std::size_t N, typename RandomAccessIterator>
std::array<ValueType<RandomAccessIterator>, N>
make_array(RandomAccessIterator first, RandomAccessIterator last) {
    // last is not relevant if we're assuming the size is N
    // I'll assert it is correct anyway
    assert(last - first == N); 
    return make_array(first, BuildIndices<N> {});
}

// usage
auto a = make_array<N>(v.begin(), v.end());

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

Собственно, это можно сделать и с помощью итераторов ввода, так как вычисление каждого элемента в скобках-init-list секвенируется перед вычислением следующего элемента (§8.5.4/4).

// internal overload with indices tag
template <std::size_t... I, typename InputIterator,
          typename Array = std::array<ValueType<InputIterator>, sizeof...(I)>>
Array make_array(InputIterator first, indices<I...>) {
    return Array { { (void(I), *first++)... } };
}    

Так как *first++ не имеет в нем I, нам нужна фиктивная I, чтобы спровоцировать расширение пакета. Comma для спасения, void(), чтобы отключить предупреждения об отсутствии эффектов, а также предотвратить перегруженные запятые.

Ответ 2

Как вы заметили, std:: array не имеет конструкторов вообще (кроме созданного компилятором конструктора по умолчанию).

Это было сделано специально, поэтому его можно статически инициализировать так же, как массив C. Если вы хотите заполнить массив без статического инициализатора, вам придется скопировать данные.

Ответ 3

Вы можете использовать BOOST_PP_ENUM как:

include <boost/preprocessor/repetition/enum.hpp>

#define INIT(z, i, v) v[i] 

std::vector<int> v;

//fill v with at least 5 items 

std::array<int,5> a = { BOOST_PP_ENUM(5, INIT, v) };  //MAGIC

Здесь последняя строка расширена следующим образом:

std::array<int,5> a = {v[0], v[1], v[2], v[3], v[4]}; //EXPANDED LINE

что вы хотите.