Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

No matching function for "f(int[4][4])" when using template "void f(int x[M][N])"?

Tags:

c++

templates

#include <iostream>

template <int M, int N>
void print1(int src[M][N]) { }

void print2(int src[4][4]) { }

int main() {

    int src[][4] = {
        { 1,  2,  3,  4},
        { 5,  6,  7,  8},
        { 9, 10, 11, 12},
        {13, 14, 15, 16},
    };

    print1(src);
    // gives error
    // error: no matching function for call to 'print1(int [4][4])'

    print2(src);
    // works!
}

In code above, print2() works as expected, but print1() gives me the error

error: no matching function for call to 'print(int [4][4])'

I don't understand, they look exactly the same, I just replaced the hard coded values to use templates so that it can accept arrays of any size.

Why does it not work? What am I doing wrong?

like image 613
Lazer Avatar asked Jun 01 '12 22:06

Lazer


3 Answers

In the declaration

void print2(int src[4][4])

The first 4 is meaningless. This function is the same as if you had declared it as

void print2(int src[][4])

or as

void print2(int (*src)[4])

This is because arrays are never passed by value in C and C++. Rather, when an array is passed to a function, it is implicitly converted to a pointer to its initial element. Likewise, when a function parameter has the type "array of T", it is transformed automatically to be of type "pointer to T." In effect, there are no array type parameters in C and C++.

So, let's consider your function template:

template <int M, int N>
void print1(int src[M][N])

Similar to print2, this function template is equivalent to:

template <int M, int N>
void print1(int src[][N])

In order to call this function without explicitly stating the template arguments in the call, the compiler must be able to deduce what M and N are from the type of the argument. M doesn't really appear anywhere in the argument list, so there's no way for it to be deduced from the argument. You could call this function by explicitly providing the template arguments when you make the call:

print1<4, 4>(src)

As we saw above, however, the compiler can deduce N itself; it's just M that it can't deduce. Therefore, you can also make the call by only providing an argument for M and letting the compiler deduce N:

print1<4>(src)

Alternatively, you could declare the function template as taking a reference to an array:

template <int M, int N>
void print1(int (&src)[M][N])

This suppresses the array-to-pointer conversion. Why? Remember that in the previous examples, the parameter was "a pointer to a single-dimensional array of int." However, in this function template, the parameter is "a reference to a two-dimensional array of int." Both extents (dimensions) are part of the type, and therefore both can be deduced by the compiler.


However, in most cases it is best to avoid multidimensional arrays and references to arrays because they are cumbersome. Neither works well with dynamic allocation, and it's often a lot of trouble to keep the array-to-pointer conversion from occurring.

like image 196
James McNellis Avatar answered Nov 19 '22 07:11

James McNellis


Use this syntax to declare your template function instead:

template <int M, int N>
void print1(int (&src)[M][N])
{
}
like image 45
coldfix Avatar answered Nov 19 '22 05:11

coldfix


A parameter declaration like int src[4] is just syntactic sugar for int *src. The same applies to arrays of arrays, so print2 is effectively:

void print2(int (*src)[4]);

That is, it takes a pointer to int[4].

For your template function, this comes down to

template <int M, int N>
void print1(int (*src)[N]) { }

Additionally, when you try to call the function, array you give as parameter also decays to a pointer to int[4].

From this, the compiler can't deduce the template parameter M and won't find a matching function.

To work around this problem you can either specify the template parameters explicitly in the call to the function, or pass the array by reference:

template <int M, int N>
void print1(int (&src)[M][N]) { }

When passing by reference the type information of the array isn't lost. The parameter declaration really specifies a reference to an array of the given dimensions, and when calling the function the passed array doesn't decay to a pointer. From this information the compiler can deduce the template parameters automatically.

like image 2
sth Avatar answered Nov 19 '22 06:11

sth