使用模板"void f(int x [M] [N])"时,"f(int [4] [4])"没有匹配函数?

Laz*_*zer 5 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!
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Jam*_*lis 11

在宣言中

void print2(int src[4][4])
Run Code Online (Sandbox Code Playgroud)

第一个4是毫无意义的.此功能与您声明的功能相同

void print2(int src[][4])
Run Code Online (Sandbox Code Playgroud)

或者作为

void print2(int (*src)[4])
Run Code Online (Sandbox Code Playgroud)

这是因为数组永远不会通过C和C++中的值传递.相反,当一个数组传递给一个函数时,它会被隐式转换为指向其初始元素的指针.同样,当函数参数的类型为"array of T"时,它会自动转换为"指向T" 的类型.实际上,C和C++中没有数组类型参数.

那么,让我们考虑一下你的功能模板:

template <int M, int N>
void print1(int src[M][N])
Run Code Online (Sandbox Code Playgroud)

print2此类似,此函数模板相当于:

template <int M, int N>
void print1(int src[][N])
Run Code Online (Sandbox Code Playgroud)

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

print1<4, 4>(src)
Run Code Online (Sandbox Code Playgroud)

然而,正如我们上面所看到的,编译器可以推断N自己; 只是M它不能推断出来.因此,您也可以通过仅为M编译器提供参数并让编译器推断来进行调用N:

print1<4>(src)
Run Code Online (Sandbox Code Playgroud)

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

template <int M, int N>
void print1(int (&src)[M][N])
Run Code Online (Sandbox Code Playgroud)

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


但是,在大多数情况下,最好避免多维数组和对数组的引用,因为它们很麻烦.这两种方法都不适用于动态分配,并且保持数组到指针转换不会发生很多麻烦.


col*_*fix 6

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

template <int M, int N>
void print1(int (&src)[M][N])
{
}
Run Code Online (Sandbox Code Playgroud)

  • 好.它在James McNellis的帖子中有所解释.使用convert-to-pointer样式时,其中一个模板参数是多余的,因此编译器现在不应该使用哪个模板.在参考样式中,两个参数都是有价值的. (2认同)