Can anyone explain me why this is true:
char *p;
short i;
long l;
(long *) p = &l ; /* Legal cast */
(long) i = l ; /* Illegal cast */
I know it has something to do with lvalue and rvalue but shouldn't (long *) p be a rvalue?
edit:
sorry it seems I confused myself and others, I asked this while reading "this MDSN" and I was surprised to see this syntax, I see it's a special feature that allows to convert lvalue into lvalue as long as it's the same size.
Neither of these expressions are legal, they should both fail to compile.
C++11, 5.17.1:
The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand and return an lvalue referring to the left operand.
5.4:
Explicit type conversion (cast notation) [expr.cast]
1 The result of the expression (T) cast-expression is of type T. The result is an lvalue if T is an lvalue reference type or an rvalue reference to function type and an xvalue if T is an rvalue reference to object type; otherwise the result is a prvalue.
So both expressions violate these constraints.
shouldn't (long *) p be a rvalue?
It is.
They're both prvalues and, as such, both statements are ill-formed:
[C++03: 5.4/1]: The result of the expression (T) cast-expression is of type T. The result is an lvalue if T is a reference type, otherwise the result is an rvalue.
[C++11: 5.4/1]: The result of the expression (T) cast-expression is of type T. The result is an lvalue if T is an lvalue reference type or an rvalue reference to function type and an xvalue if T is an rvalue reference to object type; otherwise the result is a prvalue. [..]
GCC 4.8 rejects your "legal cast", but Visual Studio has an extension that accepts this (for no apparent reason).
The result of a value conversion is an rvalue. You cannot assign to rvalues of fundamental types.
In other words, for int a = 10;, a is an lvalue of type int, but (long) a is a temporary rvalue of type long, and you cannot assign to the temporary. Likewise for pointers.
Why do you think your cast is legal? I'm getting error C2106: '=' : left operand must be l-value on both casts.
This shouldn't be legal. If you really want to do it, you have to cast it like this:
(long*&)p = &l; // equivalent to *(long**)&p = &l
But don't do that unless you know what you are doing.
Related
As I know, there have been come concepts since C++11: lvalue, rvalue, prvalue, xvalue etc.
As my understanding, if a function returns a local variable, it should be a rvalue.
std::string func() { return std::string("abc"); }
auto ret = func(); // func returns a rvalue
And for xvalue, std::move(x) is a kind of xvalue.
I think I'm right.
Today, my colleague told me that we can't get the address of rvalue, so &func() is illegal, but we can get the address of xvalue. This is a way to distinguish rvalue and xvalue...
Well, I just tried: int a = 1; std::cout << &std::move(a);. But the compiler said:
error: taking address of xvalue (rvalue reference)
So is my colleague wrong?
UPDATE
In fact, my colleague misunderstood the meaning of "has identity" and the unary & operator and I'm confused by him...
Here is a very nice question about this issue: What does it mean "xvalue has identity"?
Your colleague is incorrect. C++ has always required an lvalue for use with the address of operator. This is called out explicitly in [expr.unary.op]/3:
The operand of the unary & operator shall be an lvalue of some type T. The result is a prvalue.
If the standard had used glvalue instead of lvalue then they would have been correct but per [fig:basic.lval] lvalue and xvalue are distinct leaves of glvalue so xvalues are not allowed.
I am trying to understand and read that rvalues cannot be converted to lvalues. The following is fine
int x = 1; // x is an lvalue
int y = 2; // y is an lvalue
int z = x + y; // the "+" needs rvalues, so x and y are converted to rvalues
//(from lvalues) and an rvalue is returned
Now if I do
x = 5;
y = 6;
Aren't x and y converted to lvalues (from rvalues)?
Edit: The question in What are rvalues, lvalues, xvalues, glvalues, and prvalues? has little to no reference to conversion and no examples to explain.
It's important to be precise about what we're talking about.
In C++, an expression has a type, and can be either an lvalue or rvalue (value category)
An implicit conversion occurs when an expression of one (type, value category) occurs in a context where a different (type, value category) is expected.
An explicit conversion uses one of the language's cast operators, taking a subexpression of one (type, value category) as an operand, and yielding an expression of a different (type, value category) as the result.
An lvalue or xvalue can be implicitly converted to a prvalue; this is called the lvalue-to-rvalue conversion for historical reasons. The line
int z = x + y;
serves as an example of this implicit conversion. As the comment explains, the built-in + operator expects prvalues as its operands but we have supplied lvalues instead, so an implicit conversion occurs.
An lvalue can also be explicitly converted to an xvalue:
std::string s1 = static_cast<std::string&&>(s2); // i.e., std::move(s2)
Here s2 is an lvalue but we want to make sure the initializer is an xvalue so that the move constructor will be used, so we perform an explicit conversion.
An rvalue can be neither implicitly nor explicitly converted to an lvalue. For example, the following does not compile:
3++;
because the ++ operator requires an lvalue, and we have supplied an rvalue. Nor will the following compile, where we attempt to explicitly convert an rvalue to an lvalue:
static_cast<int&>(3)++;
(Note: the type of 3 is int, not const int. Constness is not the reason why we cannot modify 3.)
There is actually no conversion occurring in the assignments shown:
x = 5;
y = 6;
because when assigning to an int lvalue, a prvalue is expected as the right-hand operand, and a prvalue has been supplied. Although we are doing some sort of operation that has an rvalue as the source and an lvalue as the destination, this is not a conversion according to the rules of the language.
Lvalue kinda means "address". Rvalue kinda means "value". An expression is either an lvalue or an rvalue. As an expression x is an lvalue, however, in the expression z = x + y; the + operator needs data, so x and y are dereferenced. That is, the value at their addresses are fetched, and it is these rvalue values that are added together, producing an rvalue equal to their sum. The assignment operator needs an lvalue on the left and an rvalue on the right. It puts the value on the right into the address on the left, so z is not dereferenced, and is an lvalue.
A variable like x can't be said to be either an lvalue or an rvalue until you know its context.
In Effective STL item 22 Meyers appears to imply that casting a T& to a T instantiates a temporary; that is
T x;
T& y = x;
((T)(y)).doSomething();
results in doSomething() being done on a temporary rather than x.
Why must the cast result in a temporary?
Why must the cast result in a temporary?
According to the standard, (T)(y) constructs a temporary value of type T.
The behavior of the explicit type conversion using the cast notation is described in the C++11 Standard as follows:
5.4 Explicit type conversion (cast notation)
1 The result of the expression (T) cast-expression is of type T. The result is an lvalue if T is an lvalue reference type or an rvalue reference to function type and an xvalue if T is an rvalue reference to object type; otherwise the result is a prvalue.
In your case,
T is not an lvalue reference.
T is not an rvalue reference to function type.
T is not an rvalue reference to object type.
Hence, the result is a prvalue of type T.
If you look further into the definitions of prvalue and rvalue (Section 3.10), the result is a temporary object.
The temporary object is constructed using the Explicit type conversion (funtional notation).
From the C++11 Standard (emphasis mine):
5.2.3 Explicit type conversion (functional notation)
1 A simple-type-specifier (7.1.6.2) or typename-specifier (14.6) followed by a parenthesized expression-list constructs a value of the specified type given the expression list.
Let's consider this piece of code:
((T)(y)).doSomething();
Here C-style cast (T)(y) is applied.
Basically compiler performs static_cast from T& to T in this situation (look here for details).
Now from the cppreference.com static_cast:
static_cast < new_type > ( expression )
...
1) If there is an implicit conversion sequence from expression to
new_type, ... then static_cast(expression) returns the
imaginary variable Temp initialized as if by new_type
Temp(expression);
There is an implicit conversion from T& to T, so temporary is created by static_cast.
I saw an answer about lvalue yesterday, what below is its content:
— An lvalue (so called, historically, because lvalues could appear on
the left-hand side of an assignment expression) designates a function
or an object. [ Example: If E is an expression of pointer type, then
*E is an lvalue expression referring to the object or function to which E points. As another example, the result of calling a function
whose return type is an lvalue reference is an lvalue. —end example ]
so a question comes out: is reference itself a lvalue?
I know that both lvalue-reference and rvalue-reference can appear on the left-hand side of an assignment expression, but reference is neither function nor object, is there a contradiction? And Is reference really a lvalue?
Moreover, if a reference is a lvalue, what's the type of decltype(reference-expression), for example:
int main (void) { int a = 1; int &b = a; decltype((b)) c; }
what's the type of c?
In your code, the expression b is an lvalue, with type int.
The declaration of c is the same as int &c. (So it's ill-formed since you didn't provide an initializer). If you did provide an initializer, the expression c is an lvalue with type int.
I saw this kind of cast for the first time today, and I'm curious as to why this works. I thought casting in this manner would assign to the temporary, and not the class member. Using VC2010.
class A
{
public:
A() :
m_value(1.f)
{
((float)m_value) = 10.f;
}
const float m_value;
};
Even after fixing all other problems to make the code compile, it only works in VC2010 because it uses a non-standard extension. And If you specify /Wall to see all warnings, you compiler will emit
warning C4213: nonstandard extension used : cast on l-value
It shouldn't work. An explicit type conversion to float with cast notation will be a prvalue (§5.4):
The result of the expression (T) cast-expression is of type T. The result is an lvalue if T is an lvalue reference type or an rvalue reference to function type and an xvalue if T is an rvalue reference to object type; otherwise the result is a prvalue.
My emphasis added.
The assignment operator requires an lvalue as its left operand (§5.17):
All require a modifiable lvalue as their left operand and return an lvalue referring to the left operand.
A prvalue is not an lvalue.