数组到指针的转换+右值引用:重载解析GCC与clang的差异


#include <iostream>
#define FUNC() { std::cout << __PRETTY_FUNCTION__ << "n"; }
void foo(char const*&&   ) FUNC() // A
void foo(char const(&)[4]) FUNC() // B
int main()
{
foo("bar");
}

演示

当在第一个过载(A(的参数类型中使用右值引用时,clang当前主机明确地选择过载A而不是B。另一方面,GCC当前主机抱怨模糊。

我非常惊讶的是,字符串literal是4char const左值([expr.prim.literal]/1,[lex.string]/6(应该更喜欢重载A上的数组到指针转换,而不是重载B上的标识转换。

如果没有右值引用(即void foo(char const*)(,GCC和clang都会以不明确为由拒绝调用。这也是我不完全理解的,因为我猜仍然存在数组到指针的转换,因此[over.ics.rank]p3.2.1适用:

  • 如果,标准转换序列S1是比标准转换序列S2更好的转换序列

    • (3.2.1(S1是S2的适当子序列(比较[over.ics.scs]定义的规范形式的转换序列,不包括任何Lvalue转换;身份转换序列为被认为是任何非恒等式转换的子序列序列(或者如果不是

这两种情况下发生了什么?

(这只是部分答案,涵盖第二种情况(

这两种情况都发生了什么?

关于第二种情况,关于为什么以下重载

void foo(char const*     ) FUNC() // A
void foo(char const(&)[4]) FUNC() // B

产生不明确的过载(对于Clang和GCC(;[over.ics.rank]/3.2.1可能倾向于B,这是一种身份转换,而不是A,需要数组到指针的转换,这反过来又属于Lvalue Transformation的转换类别

如果,标准转换序列S1是比标准转换序列S2更好的转换序列

  • (3.2.1(S1是S2的适当子序列(比较[over.ics.scs]定义的正则形式的转换序列,排除任何Lvalue变换单位转换序列被认为是任何非单位转换序列的子序列(,或者,如果不是
  • […]

然而,正如我对上面第一个强调的段的解释一样,当应用[over.ics.rank]/3.2.1时,Lvalue Transformation:s被从序列S1和S2中排除,并且第二个强调的段只有在应用了该排除之后才适用。


正如@LanguageLawyer在一条评论中指出的那样,CWG 1789强调了规则确实允许这种模糊性,自2013年以来,该规则没有任何进展或反馈。

1789.过载分辨率下的数组引用与数组衰减

  • 章节:12.4.4.3[over.ics.rank]
  • 状态:起草
  • 提交人:费萨尔·瓦利
  • 日期:2013-10-01

当前的规则是一个类似的例子

template<class T, size_t N> void foo(T (&)[N]);
template<class T> void foo(T *t);
int arr[3]{1, 2, 3};
foo(arr);

模棱两可,即使第一个是身份匹配,第二个需要进行左值转换。这是可取的吗?

最新更新