2012-06-07 9 views
16

¿Cómo puedo inicializar un std::array de un rango (como lo define un par de iteradores)?Inicializar std :: array con un rango (par de iteradores)

Algo como esto:

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? 

pensé array podría tener un constructor que toma un par de iteradores, por lo que podía hacer array<T, N> a(v.begin(), v.end()), pero no parece tener constructores en absoluto!

Sé que puedo copy el vector en la matriz, pero prefiero inicializar la matriz con los contenidos vectoriales directamente, sin construirlos por defecto primero. ¿Cómo puedo?

+0

¿Hay alguna razón para esa preferencia? El rendimiento será casi el mismo porque el constructor predeterminado (normalmente) solo asigna las estructuras base que necesita de todos modos. No habría asignaciones, copias o liberaciones adicionales. –

+1

@DavidSchwartz: ¿Tal vez tengo un miembro de matriz const en mi clase y entonces necesito inicializarlo en la lista de inicializadores en lugar de en el cuerpo del constructor? – HighCommander4

+0

--- ¿Podemos limitarnos a iteradores de acceso aleatorio? Si es así, tengo algún tipo de solución --- No importa, no hay forma de obtener el * tamaño * en tiempo de compilación. –

Respuesta

17

Con iteradores de acceso aleatorio, y asumiendo un cierto tamaño en tiempo de compilación, se puede utilizar un pack of indices para hacerlo:

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

Esto supone un compilador capaz de elidiendo las copias intermedias. Creo que esa suposición no es una gran extensión.

En realidad, también se puede hacer con iteradores de entrada, ya que el cálculo de cada elemento en una lista de inicio reforzada se secuencia antes del cálculo del siguiente elemento (§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++)... } }; 
}  

Desde *first++ no tiene ningún I en ella, necesitamos un maniquí I para provocar la expansión paquete. Operador de comas al rescate, con void() para silenciar las advertencias sobre la falta de efectos y también evitar las comas sobrecargadas.

+0

+1. Bonito. (pero esta vez odio esta sintaxis: 'template >>>>' Quiero decir WTF.) – Nawaz

+0

Para aclarar, 'BuildIndices' es la plantilla' build_indices' en la página vinculada? ¿Podría incluirlo aquí para que esta respuesta sea independiente? – ecatmur

+0

Ver mi solución que usa Boost. – Nawaz

4

Como ha notado, std :: array no tiene constructores (excepto el constructor predeterminado generado por el compilador).

Esto se hizo a propósito, por lo que se puede inicializar estáticamente como una matriz C. Si desea llenar la matriz sin un inicializador estático, tendrá que copiar sus datos.

3

Se puede utilizar como 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 

Aquí, la última línea se expande como:

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

que es lo que desea.

Cuestiones relacionadas