Template parameter deduction from array dimensions

Score: 3.8/5 (54 votes)

The template facility in C++ doesn't only allow you to parameterise with types (such as the int in std::vector<int>), but also with values. Non-type template parameters can be of the following types[1]:

Integral (or enum) value

Pointer to object/function

Reference to object/function

Pointer to member

I'm going to look at the first of these types - integers - and how template parameter deduction behaves with arrays.

Template parameter deduction is the facility whereby the compiler determines how to instantiate a template when a template parameter is unspecified, e.g:

12

std::vector<int> vi;
std::sort(vi.begin(), vi.end());

Although we aren't specifying the type of iterator for std::sort() to use, the compiler works it out from the parameters we provide.

This confused me for some time, since I was under the impression that the template parameter was deducible from the array dimension.

(NB: as an aside, the above would also work if you wrotefun<500>(s); I think this is down to the array decaying to a pointer, which can then readily initialise the array parameter.)

Deduction of template parameters from array dimensions

Stroustrup's TCPL states that[2] "a compiler can deduce..a non-type template argument, I, from a template function argument with a type..type[I]", which implies to me that the above should work fine.
I puzzled for a while over why the parameter couldn't be deduced, and eventually hit on the answer. The standard states that a value of type "array of N T" (e.g. "array of 5 int") can be converted to an rvalue of type "pointer to T".[3] This means that the array size is lost in the instantiation, and as such the value of N cannot be deduced, the template instantiation fails, and - in our example above - fun() cannot be resolved.

The way to prevent this conversion (known as 'decay') is to declare the function parameter as a reference to an array by changing fun(string s[N]) to fun(string (&s)[N]):

The reason for this is that array decay does not happen recursively, so in the call to fun(), int[1][2] decays to a pointer to an array of 2 ints, and no further, therefore still carries the size information. (NB: I could not find authoritative evidence of this; it may be implicit in that the standard doesn't state that it should happen recursively.)
This article originally appeared at The other branch.

Footnotes

1 This is the list as specified for C++98 and 03 (cf. ISO C++ standard 14882 14.1.4); C++11 has a few additions.