This can be done, but it takes a few steps to do cleanly.  First, write a template class that represents a range of contiguous values.  Then forward a template version that knows how big the array is to the Impl version that takes this contiguous range.
Finally, implement 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) {}
  // 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 );
}
(not tested, but design should work).
Then, in your .cpp file:
void mulArrayImpl(contig_range<int> rng, const int multiplier) {
  for(auto& e : rng) {
    e *= multiplier;
  }
}
This has the downside that the code that loops over the contents of the array doesn't know (at compile time) how big the array is, which could cost optimization.  It has the advantage that the implementation doesn't have to be in the header.
Be careful about explicitly constructing a contig_range, as if you pass it a set it will assume that the set data is contiguous, which is false, and do undefined behavior all over the place.  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 being random access isn't contiguous (dangerously, it is contiguous in small chunks!), list is not even close, and the associative (ordered and unordered) containers are equally non-contiguous.
So the three constructors I implemented where std::array, std::vector and C-style arrays, which basically covers the bases.
Implementing [] is easy as well, and between for() and [] that is most of what you want an array for, isn't it?