Why template argument deduction doesn’t work in C++?

I have an issue with template arguments deduction in C++. I don’t know why the sample below doesn’t work. The sample:

#include <iostream>

template<size_t n>
void PrintArray(const int arr[n]) {
    for (int i = 0; i < n; i++)
        printf("%dn", arr[i]);
}

int main() {
    int arr[5] = {1, 2, 3, 3, 5};

    PrintArray<>(arr);
    return 0;
}

The compiler print this error:

main.cpp: In function 'int main()':
main.cpp:12:21: error: no matching function for call to 'PrintArray(int [5])'
     PrintArray<>(arr);
                     ^
main.cpp:4:6: note: candidate: template<unsigned int n> void PrintArray(const int*)
 void PrintArray(const int arr[n]) {
      ^~~~~~~~~~
main.cpp:4:6: note:   template argument deduction/substitution failed:
main.cpp:12:21: note:   couldn't deduce template parameter 'n'
     PrintArray<>(arr);

I’ve found out that the code becomes working if I pass the argument by reference, so the function signature becomes like this:

void PrintArray(const int (&arr)[n])

But why? Could you please explain me why the compiler can’t predict the array size in the first sample, when the array is passed by value?

Answer

This function declaration

void PrintArray(const int arr[n]) {

is equivalent to

void PrintArray(const int *arr) {

due to adjusting by the compiler the parameter having an array type to pointer to the array element type.

From the C++ 14 Standard (8.3.5 Functions)

5 A single name can be used for several different functions in a single scope; this is function overloading (Clause 13). All declarations for a function shall agree exactly in both the return type and the parametertype-list. The type of a function is determined using the following rules. The type of each parameter (including function parameter packs) is determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type “array of T” or “function returning T” is adjusted to be “pointer to T” or “pointer to function returning T,” respectively. After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. The resulting list of transformed parameter types and the presence or absence of the ellipsis or a function parameter pack is the function’s parameter-type-list. [ Note: This transformation does not affect the types of the parameters. For example, int()(const int p, decltype(p)) and int()(int, const int) are identical types. — end note ]

So it is impossible to deduce the template parameter n.

Declare the function like

void PrintArray(const int ( &arr )[n]) {

Or you could call your original function by specifying explicitly the template argument like

PrintArray<5>(arr);