Esto se puede hacer, pero se necesitan algunos pasos para hacerlo de manera limpia. Primero, escriba un template class
que represente un rango de valores contiguos. Luego, reenvíe una template
versión que sepa qué tan grande array
es a la Impl
versión que toma este rango contiguo.
Finalmente, implemente la contig_range
versión. Tenga en cuenta que for( int& x: range )
funciona para contig_range
, porque implementé begin()
y end()
y los punteros son iteradores.
template<typename T>
struct contig_range {
T* _begin, _end;
contig_range( T* b, T* e ):_begin(b), _end(e) {}
T const* begin() const { return _begin; }
T const* end() const { return _end; }
T* begin() { return _begin; }
T* end() { return _end; }
contig_range( contig_range const& ) = default;
contig_range( contig_range && ) = default;
contig_range():_begin(nullptr), _end(nullptr) {}
// maybe block `operator=`? contig_range follows reference semantics
// and there really isn't a run time safe `operator=` for reference semantics on
// a range when the RHS is of unknown width...
// I guess I could make it follow pointer semantics and rebase? Dunno
// this being tricky, I am tempted to =delete operator=
template<typename T, std::size_t N>
contig_range( std::array<T, N>& arr ): _begin(&*std::begin(arr)), _end(&*std::end(arr)) {}
template<typename T, std::size_t N>
contig_range( T(&arr)[N] ): _begin(&*std::begin(arr)), _end(&*std::end(arr)) {}
template<typename T, typename A>
contig_range( std::vector<T, A>& arr ): _begin(&*std::begin(arr)), _end(&*std::end(arr)) {}
};
void mulArrayImpl( contig_range<int> arr, const int multiplier );
template<std::size_t N>
void mulArray( std::array<int, N>& arr, const int multiplier ) {
mulArrayImpl( contig_range<int>(arr), multiplier );
}
(no probado, pero el diseño debería funcionar).
Luego, en su .cpp
archivo:
void mulArrayImpl(contig_range<int> rng, const int multiplier) {
for(auto& e : rng) {
e *= multiplier;
}
}
Esto tiene la desventaja de que el código que recorre el contenido de la matriz no sabe (en el momento de la compilación) qué tan grande es la matriz, lo que podría costar la optimización. Tiene la ventaja de que la implementación no tiene que estar en el encabezado.
Tenga cuidado al construir explícitamente a contig_range
, ya que si lo pasa a set
, asumirá que los set
datos son contiguos, lo cual es falso, y realizarán un comportamiento indefinido en todas partes. Los únicos dos std
contenedores en los que se garantiza que esto funcionará son vector
y array
(¡y las matrices de estilo C, como sucede!). deque
a pesar de ser el acceso aleatorio no es contiguo (peligrosamente, es contiguo en pequeños fragmentos), list
ni siquiera está cerca, y los contenedores asociativos (ordenados y desordenados) son igualmente no contiguos.
Así que los tres constructores implementé donde std::array
, std::vector
y al estilo de C matrices, que cubre básicamente las bases.
Ejecución []
es fácil también, y entre for()
y []
que es más de lo que quiere una array
para, ¿verdad?
std::vector
.