5
#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!
}

在上面的代码中,print2()按预期工作,但print1()给了我错误

错误:没有匹配的函数调用'print(int [4] [4])'

我不明白,它们看起来完全一样,我只是将硬编码值替换为使用模板,以便它可以接受任何大小的数组。

为什么它不起作用?我究竟做错了什么?

4

7 回答 7

11

在声明中

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

第一个4是没有意义的。此函数与您将其声明为一样

void print2(int src[][4])

或作为

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

这是因为在 C 和 C++ 中数组从不按值传递。相反,当将数组传递给函数时,它会隐式转换为指向其初始元素的指针。同样,当函数参数的类型为“array of T”时,它会自动转换为“pointer to T”类型。实际上,C 和 C++ 中没有数组类型参数。

因此,让我们考虑您的函数模板:

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

与 类似print2,这个函数模板等价于:

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

为了在调用中不显式声明模板参数的情况下调用此函数,编译器必须能够从参数的类型中 推断出什么M和是。并没有真正出现在参数列表中的任何位置,因此无法从参数中推断出它。您可以在调用时通过显式提供模板参数来调用此函数:NM

print1<4, 4>(src)

然而,正如我们在上面看到的,编译器可以N自行推断;只是M它无法推断。因此,您也可以通过仅提供参数M并让编译器推断来进行调用N

print1<4>(src)

或者,您可以将函数模板声明为对数组的引用:

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

这会抑制数组到指针的转换。为什么?请记住,在前面的示例中,参数是“指向 . 的一维数组的指针int”。但是,在此函数模板中,参数是“对 . 的二维数组的引用int”。两个范围(维度)都是类型的一部分,因此两者都可以由编译器推导出来。


但是,在大多数情况下,最好避免使用多维数组和对数组的引用,因为它们很麻烦。两者都不能很好地用于动态分配,而且防止数组到指针的转换通常很麻烦。

于 2012-06-01T23:01:42.543 回答
6

使用此语法来声明您的模板函数:

template <int M, int N>
void print1(int (&src)[M][N])
{
}
于 2012-06-01T22:58:48.427 回答
2

像这样的参数声明int src[4]只是int *src. 这同样适用于数组数组,因此print2有效:

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

也就是说,它需要一个指向int[4].

对于您的模板功能,这归结为

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

此外,当您尝试调用该函数时,作为参数提供的数组也会衰减为指向int[4].

由此,编译器无法推导出模板参数M,也不会找到匹配的函数。

要解决此问题,您可以在函数调用中显式指定模板参数,或者通过引用传递数组:

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

当通过引用传递时,数组的类型信息不会丢失。参数声明确实指定了对给定维度数组的引用,并且在调用函数时,传递的数组不会衰减为指针。根据这些信息,编译器可以自动推导出模板参数。

于 2012-06-01T23:01:54.290 回答
1

您必须说出要使用的模板,例如:

print1<4,4>(src);

但请注意,模板是在编译期间实例化的,因此您不能在其中放置变量(print1<k,m>(src)不起作用)。

于 2012-06-01T22:57:23.097 回答
1

您要求编译器了解编译时已知的数组大小,然后从那里推断出int模板参数。

您可以让编译器推断类型参数,但不能让编译器从您的类型(在本例中为数组大小)获取信息并推断整数模板参数。

例如,这是因为编译器将类型推断为 int[4][4]。

template <class T>
void printFoo(T t)
{
}

并且使用这样的模板当然可以:

print1<4,4>(src);
于 2012-06-01T22:57:41.657 回答
0

模板函数调用只能利用类型的模板参数推导。不能推导出非类型模板参数。

于 2012-06-02T05:06:06.480 回答
0

您应该传递模板参数。像这样:

print1<4,4>(src);
于 2012-06-01T22:57:06.790 回答