数组引用绑定与使用模板的数组到指针转换
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 转换是)。
由于不明确的重载解析,此代码示例无法编译
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 转换是)。