Say I want to write a function arrfill<N> that fills an array of length N. Below is the template implementation I tried.
template<typename T>
bool arrfill(T arr[0], T v){;}
template<size_t N, typename T>
void arrfill(T arr[N], T v){
arr[0] = v;
arrfill<N-1>(arr+1, v);
}
int main(int argc, char const *argv[])
{
bool barr[4];
arrfill<4>(barr, true);
}
However, this won't compile as the template instantiation will not terminate at the case when N is 0 and will exceed its maximum depth.
It seems like the compilers won't take the array size in the signature as the argument type. I wonder what's the proper way for specifying that?
You are bitten by argument decay.
Argument decay means that
int arr[N]is fancy talk forint* arr. TheNis utterly ignored.On top of that,
arrfill<N-1>is a call to a template function whose first argument is a size_t (or compatible). YourT arr[0]is an overload that takes as its first template argument a type. So it cannot be selected.To work around argument decay, you should take arrays by reference.
Sadly, this is undefined behavior; I am casting parts of an array into arrays of type it isn't. This happens to be undefined behavior which every single C++ compiler I have ever used consumes and does "the right thing", but it is still undefined behavior. And we should avoid that unless we have a strong reason not to; while clean and clear, clean code is not (in my opinion) a good reason to do UB. UB can come back to bite us 10 years from now when compilers update, and I don't want to maintain this code every compiler update and ensure it still works.
So really, use packs and folding.
or just use
std::fill_n.if you really, really must use recursion
does it. In c++11 we can't use if constexpr. So we do something else.
this lets us select the 0 case using overloading. We also deduce
Nautomatically.