Please consider this code:
#include
template
void f(T x) {
std::cout << sizeof(T) << \'\\n\';
}
int main
The behaviour of this code is explained by C++14 [temp.deduct.call]:
Deducing template arguments from a function call
Template argument deduction is done by comparing each function template parameter type (call it
P
) with the type of the corresponding argument of the call (call itA
) as described below
and then below:
If
P
is not a reference type:
- If
A
is an array type, the pointer type produced by the array-to-pointer standard conversion (4.2) is used in place ofA
for type deduction;
For the call f(array);
, we have A
= int[27]
. A
is an array type. So the deduced type T
is int *
, according to this last bullet point.
We can see from the qualifier "If P
is not a reference type" that this behaviour could perhaps be avoided by making P
a reference type. For the code:
template<typename T, size_t N>
void f(T (&x)[N])
the symbol P
means T(&)[N]
, which is a reference type; and it turns out that there are no conversions applied here. T
is deduced to int
, with the type of x
being int(&)[N]
.
Note that this only applies to function templates where the type is deduced from the argument. The behaviour is covered by separate parts of the specification for explicitly-provided function template parameters, and class templates.
Use the reference type for the parameter
template<typename T> void f(const T& x)
{
std::cout << sizeof(T);
}
in which case the array type will not decay.
Similarly, you can also prevent decay in your original version of f
if you explicitly specify the template agument T
as a reference-to-array type
f<int (&)[27]>(array);
In your original code sample, forcing the argument T
to have the array type (i.e. non-reference array type, by using typeof
or by specifying the type explicitly), will not prevent array type decay. While T
itself will stand for array type (as you observed), the parameter x
will still be declared as a pointer and sizeof x
will still evaluate to pointer size.
You can also use templates like the following:
template <typename T, std::size_t N>
inline std::size_t number_of_elements(T (&ary)[N]) {
return N;
}
This little trick will cause compile errors if the function is used on a non-array type.
Depending on your use case, you can work around that using references:
template<typename T>
void f(const T& x) {
std::cout << sizeof(T);
}
char a[27];
f(a);
That prints 27
, as desired.