mxxk
mxxk

Reputation: 10234

Why does array type not decay to pointer for class templates?

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

Answers (4)

Joerg S
Joerg S

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_elementswhere 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

M.M
M.M

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

mxxk
mxxk

Reputation: 10234

Well between the C and C++ standards and this answer it looks like the explanation is:

4.2 Array-to-pointer conversion

  1. An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to an rvalue of type “pointer to T.” 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

R Sahu
R Sahu

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 by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;

— otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;

— otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;

— otherwise, decltype(e) is the type of e.

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

Related Questions