3

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];
Community
  • 1
  • 1
mxxk
  • 9,514
  • 5
  • 38
  • 46
  • I think it might be because `decltype(ary)` makes it an array type, whereas template deduction would decay it to a pointer. – Weak to Enuma Elish Nov 16 '15 at 05:53
  • Thanks @JamesRoot. What's confusing especially is that the decay happens for both `template void array_template(T t)` and `template void array_template(T ary[N])` when the function is passed an array-type argument. – mxxk Nov 16 '15 at 05:57
  • I don't understand what your question is. Both your code samples are fine and there isn't any contradiction between the two. Can you perhaps show a piece of code that doesn't work but you think it should? – M.M Nov 16 '15 at 10:45
  • The question you linked is about function template deduction , whereas your second code sample is about class templates. Generally speaking, function templates and class templates have different rules. – M.M Nov 16 '15 at 11:00

4 Answers4

2

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.

Community
  • 1
  • 1
R Sahu
  • 204,454
  • 14
  • 159
  • 270
2

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 *.

Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365
0

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.

Community
  • 1
  • 1
mxxk
  • 9,514
  • 5
  • 38
  • 46
0

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)

Joerg S
  • 4,730
  • 3
  • 24
  • 43
  • It's worth clarifying that you *can* pass `std::array` (or an array that is a member of any other type of object) by value. – underscore_d Nov 18 '18 at 16:06
  • 1
    Thanks for the comment. I updated my answer to clarify that this can not be done for c arrays. – Joerg S Nov 20 '18 at 20:21