Isso pode ser feito, mas são necessárias algumas etapas para fazer de forma limpa. Primeiro, escreva um template classque represente um intervalo de valores contíguos. Em seguida, encaminhe uma templateversão que saiba o quão grande arrayé para a Implversão que leva esse intervalo contíguo.
Finalmente, implemente a contig_rangeversão. Observe que for( int& x: range )funciona para contig_range, porque implementei begin()e end()e os ponteiros são 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 );
}
(não testado, mas o design deve funcionar).
Então, em seu .cpparquivo:
void mulArrayImpl(contig_range<int> rng, const int multiplier) {
for(auto& e : rng) {
e *= multiplier;
}
}
Isso tem a desvantagem de que o código que faz um loop sobre o conteúdo do array não sabe (em tempo de compilação) o tamanho do array, o que pode custar a otimização. Tem a vantagem de que a implementação não precisa estar no cabeçalho.
Tenha cuidado ao construir explicitamente um contig_range, pois se você passá-lo um, setele assumirá que os setdados são contíguos, o que é falso, e terá um comportamento indefinido em todo o lugar. Os únicos dois stdcontêineres em que isso funciona são vectore array(e arrays de estilo C, por acaso!). dequeapesar de ser aleatório, o acesso não é contíguo (perigosamente, é contíguo em pequenos pedaços!), listnão é nem perto, e os containers associativos (ordenados e não ordenados) são igualmente não contíguos.
Assim, os três construtores I implementadas quando std::array, std::vectore C-estilo matrizes, que abrange basicamente as bases.
Implementação []é tão fácil assim, e entre for()e []que é mais do que você quer um arraypara, não é?
std::vector.