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

AnT*_*AnT 10 c++ arrays templates overload-resolution

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

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

int main()
{
  char a[] = "123";
  g(a);
}
Run Code Online (Sandbox Code Playgroud)

仔细阅读重载解析规则可以清楚为什么失败。这里没有问题。

如果我们正式将其改造为模板版本

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

int main()
{
  char a[] = "123";
  g(a);
}
Run Code Online (Sandbox Code Playgroud)

它将继续“按预期”运行,并因同样性质的模糊性而失败。到目前为止,一切都很好。

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

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

int main()
{
  char a[] = "123";
  g(a);
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

use*_*522 9

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

根据[temp.deduct.partial]/5,T &t在为部分排序执行模板参数推导期间,第一个重载的引用将被忽略。仅当两个参数都是引用类型时,以下段落才根据引用/值类别进行区分。

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

因此,第二个重载更加专门并被选择。


对于T (&t)[4]论证,两个方向的演绎都会失败,因为T[4]反对的演绎将会失败,反对A*的演绎也会失败。数组类型的数组到指针衰减是为函数调用的模板参数推导指定的,但不是为部分排序的模板参数推导指定。另请参阅有效的CWG 问题 402T*A[4]

因此,在这种情况下,两个模板都不会更加专业,并且部分排序决胜局不适用。


数组到指针的转换不相关。它不被认为比恒等转换序列更差(参见[over.ics.rank]/3.2.1不包括数组到指针转换的左值转换)。