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.
¿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. –
@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
--- ¿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. –