C++ 参数列表中char[N]和char(&;)[N]之间的差异

C++ 参数列表中char[N]和char(&;)[N]之间的差异,c++,c++11,C++,C++11,以下代码未编译: template <int N> void f(char[N]) {} int main() { char buf[10]; f(buf); } 模板 void f(char[N]){} int main(){ char-buf[10]; f(buf); } 如果我将char[N]更改为char(&)[N],它会工作。那么它们之间的区别是什么呢?显然你知道char[N]是一个数组,char(&)[N]是对char[N]的引用 当通过值作为参数传递时,c

以下代码未编译:

template <int N>
void f(char[N]) {}

int main() {
  char buf[10];
  f(buf);
}
模板
void f(char[N]){}
int main(){
char-buf[10];
f(buf);
}

如果我将
char[N]
更改为
char(&)[N]
,它会工作。那么它们之间的区别是什么呢?

显然你知道
char[N]
是一个数组,
char(&)[N]
是对
char[N]
的引用


当通过值作为参数传递时,c样式数组是特殊的。不传递数组本身,但传递引用

这种“魔力”是c++从c演变而来的历史性副作用

现在要按值传递数组,我们需要使用
std::array
,它封装了c风格的数组


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

您已经被C的向后兼容性所困扰。当您声明如下函数时:

int f(char c[10]);
您声明的函数的参数类型为
char*
。编译器将为您衰减参数类型。问题是:

int f(char c[5]);
声明相同的函数。C是这样工作的,C++保留了它的兼容性。

int f(char (&c)[10]);
声明一个函数,该函数的参数类型为“对字符数组(长度10)的引用”。C没有引用,因此不需要维护向后兼容性

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

使用不同的参数类型声明不同的函数。

我认为事件的顺序是

  • 编译器不寻找采用
    char[10]
    的函数,因为该语言不支持将数组作为值传递
  • 编译器查找引用了
    char[10]
    的函数(但没有找到任何函数)
  • 编译器查找一个函数,该函数的指针指向
    char
    ,这是所谓的类型调整后的实际参数类型,但没有找到任何参数类型
最后一点很有意思,因为模板函数
f
实际上采用了指向char的指针,正如其他海报所解释的:声明中的索引是多余的,而函数声明
f(char p[])
p
不是数组类型,而是指向char的指针类型。请注意,这不同于其他地方的此类声明(不是作为函数参数),其中
p
将是一个数组,尽管不完整

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

将函数声明为引用数组时,保留了长度信息

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


f(buf)有效。

“数组本身没有传递,但引用是。”-引用是指向第一个元素的指针。@LogicStuff如果需要,它肯定会衰减为指针,但它实际上是对对象的引用,并且(对于模板推断来说至关重要)携带类型信息-包括数组的长度。“当通过值作为参数传递时,c样式数组是特殊的”:是的——因为它们从来都不是。@PeterA.Schneider也许我的措辞是错的。将重新考虑。第一行与
template void f(char*){}相同
调整后,则无法从调用中推断出
N
。不适用于
char(&)[N]
,因此可以推断出
N
。@M.M完全正确。如果模板参数无法推断,包括这个参数,则可以显式地提供它。请参阅我的答案。为什么不使用std::array作为参数?@M.M:这些信息属于答案部分。现在我理解了问题,下面的所有答案都有意义。接受哪一个确实是一个艰难的决定。就其本身而言,您的解释并没有解释编译失败,这是关于模板参数推断的。