Is there any expression that can be evaluated to give an lvalue? I believe expressions yield an rvalue which can be assigned to an lvalue.
If you have x[y] = z;, x[y] is the lvalue and z is the rvalue. Clearly x[y] is an expression, and it can be evaluated. Therefore it can be evaluated to give an lvalue.
Gabe is correct, but more specifically, any expression that results in a reference is an lvalue. But that's not all, pointer deferences and array access are both expressions that result in an lvalue.
Another example would be a function that returns a reference, i.e. std::vector's index operator (reference operator[]( size_type n) so you can do something like this:
std::vector< Z > vector;
vector[ y ] = z;
Yet another example:
(which ? x : y) = 10;
Related
Reading through the Bjarne's CPP book I've encountered such a statement: "Where logically feasible, the result of an operator that takes an lvalue operand is an lvalue denoting that lvalue operand", and I really can't wrap my head around it. These are the examples included with this statement:
void f(int x, int y)
{
int j = x = y; // the value of x=y is the value of x after the assignment
int∗ p = &++x; // p points to x
int∗ q = &(x++); // error : x++ is not an lvalue (it is not the value stored in x)
int∗ p2 = &(x>y?x:y); // address of the int with the larger value
int& r = (x<y)?x:1; // error : 1 is not an lvalue
}
Code itself makes sense to me, but it makes sense from my personal understanding of how these operators work. But I can't really apply the statement here, for example for the first line. OK, = is an operator which can take both lvalue and rvalue operands (and from my understanding lvalue is implicitly converted to rvalue in this case) , then the result of x = y is an lvalue denoting y? If I follow this correctly, then I could write int* j = x = y, but this would be a compile-time error, as the result of x = y is clearly an rvalue. So I am really confused here.
Can anyone clarify what is this statement is semantically about and how it relates to the given examples step by step?
x = y returns lvalue referring to left-hand operand.
int* j = x = y, this is erroneous because of invalid types of the operands.
But,
int* j = &(x = y) :
works, as x = y returns l-value so this boils down to int *j = &x (value of x is y)
and similarly, this int& j = x = y also works
Additional links:
For conditional expressions: Return type of '?:' (ternary conditional operator)
so assume we write the following in C++: a=b=5; which basically means a=(b=5);
If we have a=5; I know that 5 is a literal and thus it is an R-Value. a is an L-Value. Same goes for b=5;
I'm wondering now, what happens, if we write a=b=5; respectively a=(b=5);
Can I now say the following?
For a, b=5 is an R-Value with a being an L-Value. Also, b is an L-Value and 5 is an R-Value.
What's the R-Value of a?
This depends. The built in operator= returns an lvalue reference to the left hand side of the assignment. So, if a and b are int's then (b = 5) is an lvalue expression and you assign that lvalue to a, with a and b both being lvalues and 5 being a prvalue.
This is generally the same for overloaded operator= as well since most people return a lvalue reference but it does not have to be.
If you want to cast a lvalue into a rvalue then you use std::move.
Ok, so I've been learning about lvalues and rvalues - could somebody well versed in C/C++ tell me if my thinking is correct?
Consider the code:
int i = 3;
int j = i;
Here we have two lvalues i and j, when i is assigned to j it becomes an implicit rvalue because a copy of i is stored in j. If however the code looked like:
int i = 3;
int j = &i;
Would both j and &i be lvalues because they are both physical addresses in memory? The way I understand it is that rvalues are temporary data, whereas lvalues have a physical/referenceable memory address.
Any clarification on this would be great!
The variable i never stops being an lvalue. If you can apply the address-of operator to something (like you do in the second example) then it's an lvalue.
Also in the second example &i is a pointer to i, and has the type int*. You have a type mismatch there between the declaration of j and the value you want to initialize it with.
Lastly, while i by itself is a lvalue the expression &i is not an lvalue, because you can't apply the address-of operator to it (i.e you can't do &&i).
Here we have two lvalues i and j
Yep
when i is assigned to j it becomes an implicit rvalue because a copy of i is stored in j
Not really, i is still an lvalue, but it's converted to an rvalue to read its value. This is called an lvalue-to-rvalue conversion. j is still an lvalue.
Would both j and &i be lvalues because they are both physical addresses in memory?
j is an lvalue, but &i is an rvalue; specifically a prvalue of type int*.
Being an lvalue or an rvalue is a property of an expression. Generally, all expressions which constitute a non-const qualified identifier are modifiable lvalues:
int i = 5;
i; // the expression "i" is an lvalue and is modifiable
const int j = 3;
j; // the expression "j" is still an lvalue, but not modifiable
An lvalue expression (unless qualified with const) can be modified through any of the assignment, ++ or -- operators. You can also apply the & "address-of" operator on such an expression.
Some operators like the unary *, the array subscript operator [], and the . and -> operators also yield lvalues. This means that you can say, for example, *p = 3, or a[i] = 5, or b.c++ or &c->d.
On the other hand, expressions which are not lvalues are rvalues (they are temporary and cannot be modified). If you write something like 3 = 5 or 7++, the compiler will complain that the 3 and 7 subexpressions are not lvalues.
The prefix operators return the object
itself as an lvalue. The postfix operators return a copy of the object’s original value
as an rvalue.
so in a statement like so *a++ a is being incremented and a copy of the original value of a is returned as rvalue but from the microsoft c++ language reference on Lvalues and Rvalues
An rvalue is a temporary value that does not persist beyond the expression that uses it
and gives an example
// lvalues_and_rvalues1.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
int main()
{
int x = 3 + 4;
cout << x << endl;
}
In this example, x is an lvalue because it persists beyond the expression that defines it. The expression 3 + 4 is an rvalue because it evaluates to a temporary value that does not persist beyond the expression that defines it.
My questions:
1) what is the rvalue being returned from the *a++ so that it can be dereferenced?
2) Did i misunderstand any concept ?
Thanks in advance!
The prefix operators return the object itself as an lvalue. The postfix operators return a copy of the object’s original value as an rvalue.
Wrong! Well, mostly. If the quote is talking about all prefix/suffix operators, then it's completely errated. However, if it's talking about the ++ and -- prefix/postfix pairs, then it's correct.
Now, taking that into account...
what is the rvalue returning from the *a++ so that it can be dereferenced?
Assuming a is a pointer of some kind, a++ increments a and yields a rvalue consisting of a's value before the increment. The increment and decrement operators, ++ and --, in both postfix and prefix forms, require an lvalue as their operator. This is because rvalues are temporary, that is, their scope is limited by the expression their occur in, so these operators make little or no sense on them. Remember, these operators not only inspect/read, but change/write to the variable itself.
The unary * operator takes a pointer(-like) object and dereferences it, yielding an lvalue found in there. It works for both rvalue and lvalue pointers. This is because * can be considered sort of a "passive" operator. It does not change/write to the pointer itself, but dereferences it and returns the lvalue object at the address stored by the pointer, whose address is of course that contained by the pointer. As all that * needs is the memory address contained in a pointer object, and the address of the pointer itself, if it has one at all, is useless here, * makes sense for both rvalues and lvalues.
You can think that * "requires an rvalue", and that "lvalues can be used as rvalues when necessary", if it clarifies (or confuses?) things a little bit more.
*a++ is equivalent to:
auto temp = *a;
a++;
// Use the value of temp here
except you can only refer to the value once, where as temp you could refer to multiple times.
For example:
int x[100];
void *p;
x[0] = 0x12345678;
x[1] = 0xfacecafe;
x[3] = 0xdeadbeef;
p = x;
((int *) p) ++ ;
printf("The value = 0x%08x", *(int*)p);
Compiling the above generates an lvalue required error on the line with the ++ operator.
The cast creates a temporary pointer of type int *. You can't increment a temporary as it doesn't denote a place to store the result.
In C and C++ standardese, (int *)p is an rvalue, which roughly means an expression that can only occur on the right-hand side of an assignment.
p on the other hand is an lvalue, which means it can validly appear on the left-hand side of an assignment. Only lvalues can be incremented.
The expression ((int *) p) treats the pointer stored inside the variable p is a pointer to int. If you want to treat the variable itself as a pointer to int variable (and then increment it), use a reference cast:
((int *&) p) ++ ;
Thanks to larsmans for pointing to the right direction.
I took the liberty of digging deeper into this. So for future reference, according to sections 6.5.2.4 and 6.5.4 of the C99 standard (http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf):
6.5.2.4 Postfix increment and decrement operators
Constraints
The operand of the postfix increment
or decrement operator shall have
qualified or unqualified real or
pointer type and shall be a modifiable
lvalue....
6.5.4 Cast operators
..
..
[Footnote] 89) A cast
does not yield an lvalue. Thus, a cast
to a qualified type has the same
effect as a cast to the unqualified
version of the type.
Note: This only applies to C. C++ may handle casts differently.
You can get the intended result with
p = (int*)p + 1;
Using the increment operator on a dereferenced pointer to p, which is an lvalue, also works:
(*(int**)&p)++;
However, the latter is not portable, since (void*)p might not have the same representation as (int*)p.
Rvalue expression ((int *) p) creates and temporary of type int* on which operator ++ cannot be applied.
++ requires an lvalue as its operand.
As #FredOverflow mentions lvalues and rvalues have very little to do with assignment.
Arrays are lvalues still they cannot be assigned to because they are non-modifiable.
std::string("Prasoon") is an rvalue expression still it can occur on the left side of assignment operator because we are allowed to call member functions( operator = in this case) on temporaries.