This can be done, but you need to take a few steps. First write a template class that represents a range of contiguous values. Then move the version of template , which knows how large the array for the version of Impl that accepts this continuous range.
Finally, run the contig_range version. Note that for( int& x: range ) works for contig_range because I implemented begin() and end() , and pointers are iterators.
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) {}
(not tested, but the design should work).
Then in the .cpp file:
void mulArrayImpl(contig_range<int> rng, const int multiplier) { for(auto& e : rng) { e *= multiplier; } }
This has the disadvantage that the code that iterates over the contents of the array does not know (at compile time) how large the array is, which may cost optimization. This has the advantage that the implementation does not have to be in the header.
Be careful with the explicit construction of contig_range , as if you passed it a set , it would assume that the set data is contiguous, which is false, and execute undefined behavior everywhere. The only two std containers that this is guaranteed to work on are vector and array (and C-style arrays, as it happens!). deque , despite the fact that random access is not contiguous (dangerous, it is contiguous in small pieces!), list is not even close, and associative (ordered and disordered) containers are equally disjoint.
So, there are three constructors that I implemented where std::array , std::vector and C-style arrays that basically cover the bases.
The implementation of [] also simple, and between for() and [] , which is most suitable for array , isn't it?