数组引用绑定与使用模板的数组到指针转换

Array reference binding vs. array-to-pointer conversion with templates

由于不明确的重载解析,此代码示例无法编译

void g(char (&t)[4]) {}
void g(char *t) {}

int main()
{
  char a[] = "123";
  g(a);
}

仔细阅读重载决议规则可以清楚地了解它失败的原因。这里没问题。

如果我们正式将其转化为模板版本

template <typename T> void g(T (&t)[4]) {}
template <typename T> void g(T *t) {}

int main()
{
  char a[] = "123";
  g(a);
}

它将继续“按预期”运行,并以相同性质的歧义失败。到目前为止一切顺利。

然而,下面的版本编译没有任何问题并选择了第二个重载

template <typename T> void g(T &t) {}
template <typename T> void g(T *t) {}

int main()
{
  char a[] = "123";
  g(a);
}

如果我们注释掉第二个重载,第一个重载将成功使用 T 推导为 char [4],即模板参数推导按第一个版本的预期工作,有效地使其等效至 void g(char (&t)[4])。因此,乍一看,第三个示例的行为应该与前两个示例相同。

然而,它编译。在这第三种情况下,什么 [模板] 重载解析规则起作用来挽救局面并指示编译器选择第二个重载?为什么它更喜欢数组到指针的转换而不是直接引用绑定?

P.S。我在 SO 上发现了很多针对非常相似问题的问题,但它们似乎在一些重要的细微差别上有所不同。

在函数模板的部分排序过程中,第二个重载比第一个重载更专业。

根据 [temp.deduct.partial]/5,第一个重载的 T &t 上的引用在为偏序执行的模板参数推导过程中被忽略。以下段落仅在两个参数均为引用类型时才根据 reference/value 类别进行区分。

然后第一个重载的 T 总是可以推导从第二个重载的参数发明的类型 A*,但是第二个重载的 T* 不能推导从第一个重载的参数发明的类型 A

因此第二个重载更专业,因此被选中。


带有 T (&t)[4] 参数的双向推导都会失败,因为 T[4]A* 的推导将失败,T*A[4] 的推导也会失败. Array-to-pointer 数组类型的衰减指定用于函数调用的模板参数推导,但不用于部分排序的模板参数推导。另见活动 CWG issue 402.

所以在这种情况下,这两个模板都不会更专业,部分排序决胜局不适用。


array-to-pointer 转换不相关。它不被认为比恒等转换序列差(参见 [over.ics.rank]/3.2.1 不包括 左值转换 ,其中 array-to-pointer 转换是)。