Isso pode ser feito, mas são necessárias algumas etapas para fazer de forma limpa. Primeiro, escreva um template class
que represente um intervalo de valores contíguos. Em seguida, encaminhe uma template
versão que saiba o quão grande array
é para a Impl
versão que leva esse intervalo contíguo.
Finalmente, implemente a contig_range
versã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 .cpp
arquivo:
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, set
ele assumirá que os set
dados são contíguos, o que é falso, e terá um comportamento indefinido em todo o lugar. Os únicos dois std
contêineres em que isso funciona são vector
e array
(e arrays de estilo C, por acaso!). deque
apesar de ser aleatório, o acesso não é contíguo (perigosamente, é contíguo em pequenos pedaços!), list
nã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::vector
e 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 array
para, não é?
std::vector
.