是`f()。a [0]`一个xvalue?

时间:2015-07-03 12:32:55

标签: c++

struct S{
    int a[3] = {1,2,3};
};

S&& f(){return S();}

&f().a;       //[Error] taking address of xvalue (rvalue reference)
&f().a[0];    //ok in GCC 5.1.0 and Clang 3.6.0

S s;
&static_cast<S&&>(s).a;     //[Error] taking address of xvalue (rvalue reference)
&static_cast<S&&>(s).a[0];  //ok in GCC 5.1.0 and Clang 3.6.0
  

5.7表达式是xvalue,如果它是:

     

(7.1) - 调用函数的结果,无论是隐式还是显式,其返回类型是对象类型的右值引用,

     

(7.2) - 对对象类型的右值引用的强制转换,

     

(7.3) - 一个类成员访问表达式,指定非引用类型的非静态数据成员,其中对象表达式是xvalue,或

     

(7.4) - a。*指向成员的表达式,其中第一个操作数是xvalue,第二个操作数是指向数据成员的指针。

     

5.2.1订阅    后缀表达式后跟方括号中的表达式是后缀表达式。其中一个表达方式   应具有“T”数组或“指向T的指针”类型,另一个应具有无范围的枚举   或整体式。结果是“T”类型。类型“T”应是完全定义的对象类型。表达式E1[E2]*((E1)+(E2))<<*t相同(根据定义)[注:有关*+的详细信息,请参见5.3和5.7   f().a[0]和8.3.4有关数组的详细信息。 -end note],,除了在数组操作数的情况下,如果该操作数是左值,则结果为左值,否则为xvalue。

那么,f().a[0]是xvalue吗?

我认为&f().a;应该是xvalue。

[EDIT1]

忽略&f().a[0];static_cast<S&&>(s).a因为12.2 [class.temporary] p5.2

  

函数返回语句(6.6.3)中返回值的临时绑定的生存期不是   扩展;临时在return语句中的完整表达式结束时被销毁

static_cast<S&&>(s).a[0]是xvalue(7.2和7.3)。

&#34; 除了在数组操作数的情况下,如果该操作数是左值,则结果为左值,否则为x值。 &#34;

所以我认为&static_cast<S&&>(s).a[0]; //ok in GCC 5.1.0 and Clang 3.6.0应该是xvalue,但是

GetHashCode

追问:

我错了吗?如果我错了,请告诉我一个例子,即下载一个数组会产生一个xvalue。

2 个答案:

答案 0 :(得分:2)

据我所知,这确实是正确的,这看起来像是&#34;错误&#34; ,尽管最近公平地改变了CWG defect 1213,其中说:< / p>

  

因为下标操作被定义为通过指针值的间接,所以应用于xvalue数组的下标运算符的结果是左值,而不是xvalue。这对某些人来说可能是令人惊讶的。

这改变了5.2.1 [expr.sub]部分如下:

  

后缀表达式后跟方括号中的表达式是a   后缀表达。其中一个表达式应具有该类型   “T”数组或“指向T”的指针,另一个应具有无范围枚举或整数类型。结果是类型的左值   “T.”类型“T”应该是一个完全定义的对象类型.62   表达式E1 [E2]与*((E1)+(E2))相同(按照定义)[注:   有关*和+和的详细信息,请参见5.3 [expr.unary]和5.7 [expr.add]   8.3.4 [dcl.array]了解数组的详细信息。 -end note] ,除了在数组操作数的情况下,如果该操作数,结果是左值   否则是左值和x值。

所以f().a[0];static_cast<S&&>(s).a[0]的结果确实应该是xvalues。

此缺陷在2012年12月之前没有提出解决方案,clangs defect report support列出了该缺陷报告的支持未知,因此很可能实施者尚未解决此缺陷。

更新

提起了一个铿锵声bug report: Subscript operator applied to an temporary array results in an lvalue

答案 1 :(得分:0)

我可以在Clang 3.4.1中测试std = c ++ 11。

以下是我的结论:

int i = f().a[0]是正确的:您获得对临时结构的引用,临时的生命周期在引用期间延长,您采用:罚款。< / p> 编译器接受

int *i = &f().a[0]。但是,f上的警告表示您返回对本地临时对象的引用在此处有意义。临时对象的生命周期在引用期间延长:这里是复制地址的时间。一旦你取了a的地址,包含的对象就会消失,你只有一个悬空引用

int *i = f().a与之前的情况完全相同。

但是当你执行&f().a时,你正在使用类型&#39; int [3]&#39; 的rvalue的地址,并且它没有意义拿这样一个地址:你只能接受它的价值。

让我们更进一步:

S s = f();是正确的。你得到一个临时结构的引用,临时的生命周期在引用的持续时间内延长,你采用:罚款。

现在&s.a[0]是指向int的定义明确的指针,int *ix2 = &static_cast<S&&>(s).a[0];

您甚至可以写:int (*ix3)[3] = &s.a;将数组的地址设为3 int,但仍然出于同样的原因,您无法写&static_cast<S&&>(s).a因为您将类型&#39; int [3]&#39;

的右值的地址

TL / DR

S s = f(); s.a是一个定义良好的左值,s.a[0]是定义良好的左值(您可以写s.a[0] = 5;)。

f().s是一个右值,但使用它会调用UB,因为它以对临时对象的引用结束,在你使用它之前它将被销毁。

f().s[0]可以用作明确定义的右值。您可以将它用作左值,但出于与上述相同的原因,它将调用UB。