Reputation: 10234
We know that arrays decay to pointers in function templates and to take an array type parameter we need to declare our function template with a reference-to-array:
template<class T, std::size_t N>
std::size_t number_of_elements(T (&ary)[N]) {
return N;
}
However, why do we not need to declare reference-to-array parameters in class templates? The code below shows this and compiles under C++11.
template<class T>
struct cls_number_of_elements {};
template<class T, std::size_t R>
struct cls_number_of_elements<T[R]> {
static const int N = R;
};
char ary[] = "12345";
auto const N = cls_number_of_elements<decltype(ary)>::N;
char ar2[N];
Upvotes: 3
Views: 945
Reputation: 5129
Looking at the code I have a very short explanation which doesn't have anything to do with the differences between class and function templates:
For number_of_elements
the "type" of the array is T[N]
. Sample applies to cls_number_of_elements
where the "type" is T[R]
.
In both cases the T
will become char
while N
resp. R
becomes 6
.
The only reason why you need the &
is because you cannot pass c arrays by value.
(see anser from @bku_drytt)
Upvotes: 0
Reputation: 141554
The "decay" you are talking about is something that happens when deducing a function template parameter from an argument. I have posted a fuller explanation here.
When you explicitly provide a value for a template parameter, there is no deduction step; the value you explicitly provided is exactly the value that the parameter takes.
With class templates, there is never parameter deduction; they must always have their parameters explicitly provided.
Illustrative examples:
template<typename T> void f(T t) {}
template<typename T> struct S { void f(T t) {} };
...
int x[27];
f(x); // Type deduction: decay occurs, T = int *
f<int *>(x); // No deduction. T = int *
f<int[27]>(x); // No deduction. T = int[27]
S<int[27]>().f(x); // No deduction. T = int[27]
In the latter two cases, adjustment still occurs. [temp.deduct]/3 explicitly restates this: when T
is an array type, the function parameter T t
means that t
has a pointer type, in exactly the same fashion that:
void g(int t[27])
actually specifies that t
has type int *
.
Upvotes: 2
Reputation: 10234
Well between the C and C++ standards and this answer it looks like the explanation is:
4.2 Array-to-pointer conversion
- An lvalue or rvalue of type “array of
N
T
” or “array of unknown bound ofT
” can be converted to an rvalue of type “pointer toT
.” The result is a pointer to the first element of the array.
Looks like this conversion does happen for deduced template arguments in a function template
number_of_elements(ary)
but not for template arguments in class templates
cls_number_of_elements<char[5]>
and explicitly-typed function templates
number_of_elements<char[5]>(ary)
since those types are not deduced.
Upvotes: 0
Reputation: 206577
From the C++11 Standard:
7.1.6.2 Simple type specifiers
4 The type denoted by
decltype(e)
is defined as follows:— if
e
is an unparenthesized id-expression or an unparenthesized class member access (5.2.5),decltype(e)
is the type of the entity named bye
. If there is no such entity, or ife
names a set of overloaded functions, the program is ill-formed;— otherwise, if
e
is an xvalue,decltype(e)
isT&&
, whereT
is the type ofe
;— otherwise, if
e
is an lvalue,decltype(e)
isT&
, whereT
is the type ofe
;— otherwise,
decltype(e)
is the type ofe
.
Given
char ary[] = "12345";
decltype(ary)
denotes the type of ary
(an unparenthesized id-expression), which is char[6]
.
A more user friendly description of decltype
can be found at http://en.cppreference.com.
Upvotes: 2