5

下面的代码可以很好地编译g++ (GCC) 4.7.1 20120721,但在最近构建时失败clang version 3.2 (trunk)

struct Y {};

struct X {
  operator const Y() const { return Y(); }
};

void f(Y&& y) {}

int main()
{
  f(X());
  return 0;
}

将转换运算符更改operator Y() const为足以使代码在两个编译器上编译。

在这种情况下,哪个编译器实际上是符合标准的?标准实际上对此有何评论?

要求的逐字错误:

bla.cpp:14:5: error: no viable conversion from 'X' to 'Y'
  f(X());
    ^~~
bla.cpp:1:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'X' to
      'const Y &' for 1st argument
struct Y {
       ^
bla.cpp:1:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'X' to
      'Y &&' for 1st argument
struct Y {
       ^
bla.cpp:6:3: note: candidate function
  operator const Y() const { return Y(); }
  ^
bla.cpp:10:12: note: passing argument to parameter 'y' here
void f(Y&& y) {}
       ^

编辑:不幸的是,即使添加了重载

void f(const Y&) {}

仍然让 clang 选择右值引用重载,因此这会破坏用于编译的现有代码,例如使用标准容器。

4

2 回答 2

6

我相信clang拒绝这一点是正确的。将参数传递给f(Y&&)需要两个转换步骤,第一个是 your operator const Y(),第二个是Y's 的复制构造函数。我认为两者都算作用户定义的转换,并且都是隐式的,这违反了隐式转换序列仅包含一个用户定义的转换的原则。

这个按 const 值返回的目的是什么?包含一些关于返回 a 的语义的有趣见解const T

嗯,如果我尝试void f(const Y&y)像现在编辑的问题那样添加重载,clang 的行为会非常奇怪。它仍然抱怨无法转换XY,甚至没有f(const Y& y)在其诊断中列出过载。但是,一旦我将重载更改为Y按值获取,即 write void f(const Y y),它就会抱怨调用f模棱两可。

这是 XCode 4.5 的 clang 报告Apple clang version 4.1 (tags/Apple/clang-421.11.66) (based on LLVM 3.1svn). 如果您可以使用原版 clang 重现此问题,您可能应该在 clang 邮件列表中报告此问题 - 确实似乎某个地方潜伏着一个错误......

于 2012-10-08T11:16:43.263 回答
2

该示例格式不正确。

一些 N3242 报价。

8.5.3 第 4 段:

给定类型“<em>cv1 T1”和“<em>cv2” ,如果“<em>cv1”与“<em>cv2 ”类型相同,或者是“<em>cv2”的基类,则“<em>cv1”与“< T2em>cv2 T1”引用相关。如果“<em>cv1 ”与“<em>cv2”引用相关,并且 cv1 与 cv2 具有相同的 cv-qualification 或大于 cv2 的 cv-qualification,则“<em>cv1”与“<em> cv2 ”是引用兼容T2T1T2T1T2T1T2T1T2

第 5 段(子弹标签是我的):

对“ cv1 ”类型的引用由“ cv2 T1 ”类型的表达式初始化,如下所示: T2

  1. 如果引用是左值引用并且...
  2. 否则,该引用应为对非易失性 const 类型的左值引用(即cv1应为const),或者该引用应为右值引用。

    一种。如果初始化表达式

    • 一世。是一个 xvalue、类纯右值、数组纯右值或函数左值,并且“cv1”与“ cv2 T1引用兼容,或者 T2
    • ii. 具有类类型(即T2是类类型),其中T1与 不引用相关T2,并且可以隐式转换为类型为“ cv3 T3 ”的xvalue、类纯右值或函数左值,其中“ cv1 T1 ”与引用兼容“ cv3 T3 ”,
    • 然后引用绑定到....

    湾。否则,将使用非引用复制初始化 (8.5) 的规则从初始化表达式创建并初始化一个临时类型“ cv1 ”。 T1然后将引用绑定到临时文件。如果T1与 相关T2则 cv1应与 cv2 具有相同的 cv-qualification 或大于cv2的 cv-qualification 。...

对于函数参数初始化,T1is Y, T2is X, cv1cv2都是空的。1 出来了:引用是右值引用,而不是左值引用。2.ai 已淘汰:XY. 2.b。因为Y从类型的纯右值复制初始化 anX涉及两个用户定义的转换:转换函数,然后是Y. (确切的禁令在 13.3.3.1p4 中。)

对于案例 2.a.ii.,“ cv3 T3 ”的明显选择是const Y,但这并不好,因为Y它与const Y. 您可能会主张尝试T3isY并且cv3为空,但随后您又需要复制构造函数Y作为第二个隐式用户定义转换。

于 2012-10-08T13:02:07.620 回答