2012-06-07 3 views
16

Как я могу инициализировать 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()), но это, кажется, не имеют конструкторов на всех!

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

+0

Есть ли какая-то причина для этого предпочтения? Производительность будет почти точно такой же, поскольку конструктор по умолчанию (обычно) выделяет только базовые структуры, которые вам нужны. Никакого дополнительного выделения, копирования или освобождения не будет. –

+1

@DavidSchwartz: Возможно, у меня есть член const array в моем классе, и поэтому мне нужно инициализировать его в списке инициализаторов, а не в корпусе конструктора? – HighCommander4

+0

--- Можем ли мы ограничить себя итераторами произвольного доступа? Если это так, у меня есть какое-то решение --- Nevermind, нет способа получить * размер * во время компиляции. –

ответ

17

С случайными итераторами доступа, и при условии определенного размера во время компиляции, вы можете использовать pack of indices сделать так:

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

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

На самом деле это можно сделать и с помощью итераторов ввода, так как вычисление каждого элемента в скобках-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(), чтобы отключить предупреждения об отсутствии эффектов, а также предотвратить перегруженные запятые.

+0

+1. Ницца. (но на этот раз я ненавижу этот синтаксис: 'template >>>>'. Я имею в виду WTF.) – Nawaz

+0

Чтобы уточнить, 'BuildIndices' является шаблоном' build_indices' на связанной странице? Не могли бы вы включить его здесь, чтобы сделать этот ответ автономным? – ecatmur

+0

Посмотрите мое решение, которое использует Boost. – Nawaz

4

Как вы уже заметили, 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 

который, что вы хотите.

Смежные вопросы