参数列表中char[N]和char(&)[N]的区别

5
下面的代码无法编译:
template <int N>
void f(char[N]) {}

int main() {
  char buf[10];
  f(buf);
}

如果我将char[N]更改为char (&)[N],它就可以工作了。那么它们之间有何区别?

1
经过调整,第一行与template <int N> void f(char *) {}相同,然后无法从调用中推断出N。对于char(&)[N],不适用任何调整,因此可以推断出N - M.M
@M.M 没错。在无法推导模板参数的情况下,包括这种情况,我们总是可以显式地提供它。请参见我的答案。 - Peter - Reinstate Monica
为什么不使用std :: array作为参数呢? - Erik Alapää
@M.M:那些信息应该放在“答案”部分。 - Lightness Races in Orbit
现在我明白了问题,所有下面的答案都很有道理。这确实是一个难以决定要接受哪个答案的问题。 - Kan Li
3个回答

7
你被 C 语言的向后兼容性所困扰。当你声明一个函数时,如下所示:
int f(char c[10]);

你声明了一个参数类型为char *的函数。编译器会自动将参数类型转换。问题在于:
int f(char c[5]);

声明相同的函数。这是C语言的工作方式,C ++为了兼容性而保留了它。

int f(char (&c)[10]);

声明一个函数,其参数类型为“char数组(长度为10)的引用”。C语言没有引用,因此无需保持向后兼容性。
int f(char (&c)[5]);

声明了一个不同的函数 - 具有不同的参数类型。

就本身而言,您的解释并没有详细说明编译失败的原因,这是关于模板参数推导的问题。 - Lightness Races in Orbit

2
显然,您已经知道char [N]是一个数组,而char (&)[N]是对char [N]的引用。

C风格的数组在按值传递参数时是特殊的。数组本身不会被传递,但是会传递一个引用。

这种“魔法”是C++从C演变而来的历史副作用。

现在,要通过值传递数组,我们可以使用封装了C风格数组的std::array<char, N>

请注意,char (&)[N]被视为字面类型,因此可以在constexpr上下文中将其传递给constexpr函数。


数组本身并没有被传递,而是传递了一个引用 - 引用指向第一个元素的指针。 - LogicStuff
@LogicStuff 如果需要,它肯定会衰减为指针,但实际上它是一个对象的引用,并且(对于模板推导至关重要)携带类型信息 - 包括数组的长度。 - Richard Hodges
2
“C风格数组作为参数按值传递时是特殊的”:不,因为它们根本不会这样。 - Peter - Reinstate Monica
@PeterA.Schneider,也许我的措辞不太妥当。我会重新考虑的。 - Richard Hodges

2
我认为事件的顺序如下:
  • 编译器不会查找函数,其参数是char[10],因为语言不支持传递数组作为值。
  • 编译器会查找需要一个char[10] 的引用的函数(并没有找到任何一个)。
  • 编译器会查找需要char指针的函数,这是在所谓的类型调整之后的实际参数类型,并且没有找到任何一个。

最后一点很有趣,因为模板函数f实际上确实需要一个指向char的指针,就像其他帖子中解释的那样:声明中的索引是多余的,在函数声明f(char p[])中,p不是数组类型,而是指向char的指针类型。请注意,这与其他地方(不作为函数参数)的声明不同,那里的p将是一个数组,尽管是不完全的。

编译器不能实例化函数模板的原因不是其参数类型错误:经过参数类型调整,它将匹配。原因仅仅是它无法从参数中推断出模板参数N。毕竟,对于每个N都会有一个不同的f:编译器应该采用哪一个?在实际参数buf被“调整”为指向其第一个元素之后,该参数中的长度信息将丢失。 (是的,编译器很愚蠢。)

当您将函数声明为引用数组时,长度信息已保留。

另一种可能性是显式实例化模板函数:

f<10>(buf);有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接