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.7f().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。
答案 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。