Is `f().a[0]` an xvalue?

后端 未结 2 1907
夕颜
夕颜 2020-12-10 03:05
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         


        
相关标签:
2条回答
  • 2020-12-10 03:47

    As far as I can tell you are indeed correct, this looks a "bug", although to be fair this changed recently with CWG defect 1213 which says:

    Because the subscripting operation is defined as indirection through a pointer value, the result of a subscript operator applied to an xvalue array is an lvalue, not an xvalue. This could be surprising to some.

    and this changed section 5.2.1 [expr.sub] as follows:

    A postfix expression followed by an expression in square brackets is a postfix expression. One of the expressions shall have the type “array of T” or “pointer to T” and the other shall have unscoped enumeration or integral type. The result is an lvalue of type “T.” The type “T” shall be a completely-defined object type.62 The expression E1[E2] is identical (by definition) to *((E1)+(E2)) [Note: see 5.3 [expr.unary] and 5.7 [expr.add] for details of * and + and 8.3.4 [dcl.array] for details of arrays. —end note], except that in the case of an array operand, the result is an lvalue if that operand is an lvalue and an xvalue otherwise.

    So indeed the result of f().a[0]; and static_cast<S&&>(s).a[0] should be xvalues.

    This defect did not have a proposed resolution until December 2012 and clangs defect report support lists the support of that defect report as unknown so most likely the implementers have not gotten to fixing this defect yet.

    Update

    Filed a clang bug report: Subscript operator applied to an temporary array results in an lvalue.

    0 讨论(0)
  • 2020-12-10 03:57

    I could test in Clang 3.4.1 std=c++11.

    Here are my conclusions :

    int i = f().a[0] would be correct : you get a reference to a temporary struct, the lifetime of the temporary is extended for the duration of the reference, you take a value : fine.

    int *i = &f().a[0] is accepted by the compiler. However, the warning on f saying that you are returning reference to local temporary object makes sense here. The lifetime of the temporary object is extended for the duration of the reference : here the time to copy the address. As soon as you have taken the address of a, the containing object vanishes and you only have a dangling reference.

    int *i = f().a is exactly same case as previous one.

    But when you do &f().a, you are taking the address of an rvalue of type 'int [3]', and it does not make sense to take such an address : you can only take its value.

    Let's go one step further :

    S s = f(); is correct. You get a reference to a temporary struct, the lifetime of the temporary is extended for the duration of the reference, you take a value : fine.

    Now &s.a[0] is a well defined pointer to int, as is int *ix2 = &static_cast<S&&>(s).a[0];

    You can even write : int (*ix3)[3] = &s.a; to take the address of an array to 3 int, but still for same reason, you cannot write &static_cast<S&&>(s).a because you would take the address of an rvalue of type 'int [3]'

    TL/DR

    With S s = f(); s.a is a well defined rvalue, s.a[0] is a well defined lvalue (you can write s.a[0] = 5;).

    f().s is a rvalue, but using it will invoke UB, because it ends in a reference to a temporary object that will be destroyed before you can use it.

    f().s[0] can be used as a well defined rvalue. You can use it as a lvalue, but for the same reason as above, it would invoke UB.

    0 讨论(0)
提交回复
热议问题