Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a temporary array as an lvalue

This program is ill-formed:

struct X { int i; };

int main() {
    (X { }).i = 1;
}

i, a sub-object of the temporary X { }, cannot be used as an lvalue because X { } is an rvalue.

However, this silently compiles with GCC 5.2.1 and -Wall:

using Y = int[10];

int main() {
    (Y { })[0] = 1;
}

If the compiler is correct, then this time, the zeroth element of (Y { }), which is a subobject of (Y { }), can be treated as an lvalue.

My questions are:

  1. Is the second program ill-formed?
  2. Why (not), even though both programs seem to treat a subobject of a temporary as an lvalue?
like image 616
J. Doe Avatar asked Oct 16 '15 01:10

J. Doe


1 Answers

I believe the second case is ill-formed, if I am reading defect report 1213 correctly, it 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 the resolution was the following changes to the draft C++ standard section 5.2.1 [expr.sub], (bolded sections were added and strikethroughs were removed):

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.

The result of Y{} is a prvalue from section 5.2.3 [expr.type.conv]:

Similarly, a simple-type-specifier or typename-specifier followed by a braced-init-list creates a temporary object of the specified type direct-list-initialized (8.5.4) with the specified braced-init-list, and its value is that temporary object as a prvalue.

So the result of (Y { })[0] should be an xvalue and therefore ill-formed since assignment requires a modifiable lvalue on the left operand:

The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand [...]

We can find the updated wording from the defect report in the draft C++14 standard, so this change was applied after C++11 but may apply to C++11 since this was a applied via a defect report.

Why section 5.3.1 [expr.unary.op] was not also updated is unclear to me, it seems somewhat inconsistent to say the result is an lvalue which it is not in all cases.

Update

Filed a clang bug report.

like image 129
Shafik Yaghmour Avatar answered Oct 11 '22 02:10

Shafik Yaghmour