Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Aren't elements of a temporary array rvalues themselves?

using intArray = int[];
int (&a) [4] = intArray{1, 2, 3, 4};

This is not allowed since it's illegal to bind a non-const lvalue reference to a temporary (rvalue). Both g++ 4.9.1 and clang 3.4.2 barks back with errors; it compiles fine when a is const qualified

int const (&a) [4] = intArray{1, 2, 3, 4};

However, when I do this

int &x = intArray{1, 2, 3, 4} [1];

both compilers compile it fine without an error. Digging the standard (draft N3337) for this, §5.2.1 Subscripting says

1 A postfix expression followed by an expression in square brackets is a postfix expression. One of the expressions shall have the type “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. The expression E1[E2] is identical (by definition) to *((E1)+(E2))

2 A braced-init-list shall not be used with the built-in subscript operator.

  1. If I go with 1 then I don't see why the standard allows temporary arrays to be constructed since subscripting an element in it would give out an lvalue i.e. I can get a lvalue from a temporary which contradicts the original notion of temporaries can only be bound to const lvalue references or rvalue references.

  2. If I go with 2 then why do the compilers not throw an error when I'm doing {1, 2, 3, 4}[1]?

like image 789
legends2k Avatar asked Jul 17 '14 09:07

legends2k


1 Answers

Question 1

The rule about not binding temporaries to lvalues doesn't provide ironclad safety. It prevents part of this class of errors, but not all. I suspect that to prevent all such errors, the notion of "temporariness" would need to be incorporated into the type system, just like const. Then you could "cast away temporariness" in cases where you know you aren't going to keep the reference for longer than the lifetime of the temporary. The committee has decided that the rule we have is worth it, presumably they've also decided that going to further effort isn't worth it.

For another example, vector<int>(4)[0] also returns an lvalue even though the operator[] call was made on a temporary. The standard isn't going to forbid temporary vectors to be constructed because of this, and I don't see that it should forbid temporary arrays either. OK, so vector is a user-defined type whereas arrays are built-in, but aside from that I think the situations are similar.

If you use arrays at all, and especially temporary ones, then to some extent the standard thinks you get what you deserve. It's not going to forbid temporary arrays just because it's possible to get an lvalue out of one.

I think you have a valid general point, though. Subscript probably could be defined more safely on array rvalues, since the compiler has the necessary information. It could evaluate to an rvalue whose value is that of the corresponding array element. That might be confusing or inconvenient, since its not consistent with the usual subscript expressions, but it would be safer :-) If you write struct A {int a;}, then A().a is an rvalue, so I don't think it would be completely out of the question to apply that principle to arrays. It would be a breaking change of course.

Question 2

You aren't using a subscript on a braced-init-list. You're using it on a temporary that happens to have been constructed using new-style initializer syntax. That is to say, your expression parses (intArray{1, 2, 3, 4})[1], not intArray({1, 2, 3, 4}[1]).

like image 91
Steve Jessop Avatar answered Sep 24 '22 02:09

Steve Jessop