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

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

本文关键字:int 功能 void      更新时间:2023-10-16
#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()给了我错误

错误:调用"打印(int [4][4])"没有匹配函数

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

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

在声明中

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

第一个4毫无意义。 此函数与将其声明

void print2(int src[][4])

或作为

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

这是因为数组永远不会按 C 和 C++ 中的值传递。 相反,当数组传递给函数时,它会隐式转换为指向其初始元素的指针。 同样,当函数参数的类型为"T数组"时,它会自动转换为"指向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])

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

print1<4, 4>(src)

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

print1<4>(src)

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

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

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


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

改用以下语法声明模板函数:

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

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]) { }

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

你必须

说出你想使用哪个模板,例如:

print1<4,4>(src);

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

你要求编译器获取有关编译时已知数组大小的知识,然后从那里推断出 int 模板参数。

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

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

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

像这样使用模板当然有效:

print1<4,4>(src);

您应该传递模板参数。喜欢这个:

print1<4,4>(src);

模板函数调用只能利用类型的模板参数推导。无法推断非类型模板参数。