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.
Related
I have a bit confusion about this code:
struct A
{
A& bar()&&;
};
A& A::bar()&&
{
std::cout << "A::bar()&&\n";
return *this;
}
int main()
{
A{}.bar();// called by an rvalue
}
So what I understand is that bar can be called only by a modifiable-rvalue. Until this it is OK. But how can bar return a non-constant lvalue reference to that rvalue?
How bar() binds and returns a modifiable lvalue reference to that rvalue object?
The reason is that the this pointer for a class C can be either C* or const C* - not C& * or C&& * (those aren't actual types; you can't declare a C& * ptr). So, even when your method runs for an rvalue instance of class A, you get one of those two (GodBolt). And when you apply the * operator, you get an lvalue, not an rvalue.
This has to do with [expr.unary.op]/1
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points. If the type of the expression is “pointer to T”, the type of the result is “T”. [ Note: Indirection through a pointer to an incomplete type (other than cv void) is valid. The lvalue thus obtained can be used in limited ways (to initialize a reference, for example); this lvalue must not be converted to a prvalue, see [conv.lval]. — end note ]
emphasis mine
So when you dereference this yo get an lvalue. It doesn't matter if this is pointing to a temporary object or not, you will always get an lvalue. Since *this is an lvalue, you are legally allowed to return an lvalue reference, the program in syntactically correct. Semantically it is not, but that is a lot harder to test for and is often not something that is diagnosed as it requires quite a bit of static analysis.
It would be cool if the language could be updated where * only yields an lvalue when applied to this in a non-rvalue qualified function.
For the following code, all but the last assertion passes:
template<typename T>
constexpr void assert_static_cast_identity() {
using T_cast = decltype(static_cast<T>(std::declval<T>()));
static_assert(std::is_same_v<T_cast, T>);
}
int main() {
assert_static_cast_identity<int>();
assert_static_cast_identity<int&>();
assert_static_cast_identity<int&&>();
// assert_static_cast_identity<int(int)>(); // illegal cast
assert_static_cast_identity<int (&)(int)>();
assert_static_cast_identity<int (&&)(int)>(); // static assert fails
}
Why is this last assertion failing, and static_cast<T> not always returning a T?
This is hard-coded in the definition of static_cast:
[expr.static.cast] (emphasis mine)
1 The result of the expression static_cast<T>(v) is the result
of converting the expression v to type T. If T is an lvalue
reference type or an rvalue reference to function type, the result is
an lvalue; if T is an rvalue reference to object type, the result is
an xvalue; otherwise, the result is a prvalue. The static_cast
operator shall not cast away constness.
decltype respects the value category of its operand, and produces an lvalue reference for lvalue expressions.
The reasoning may be due to function names themselves always being lvalues, and so an rvalue of a function type cannot appear "in the wild". As such, casting to that type probably makes little sense.
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.
class A{
public:
virtual ~A() {};
};
class B : public A{ };
int main(){
A&& p = B();
dynamic_cast<B&&>(std::move(p));
}
Throws the error (g++ 5.2.0):
error: conversion to non-const reference type 'std::remove_reference<A&>::type& {aka class A&}' from rvalue of type 'A' [-fpermissive]
It attempts to cast std::move(p) to type A&, but I cannot figure out why. I would've thought it necessary to cast p as an rvalue before converting to an rvalue reference, but if I remove std::move it compiles fine. From cppreference:
dynamic_cast < new_type > ( expression )
Similar to other cast expressions, the result is:
an lvalue if new_type is an lvalue reference type (expression must be an lvalue)
an xvalue if new_type is an rvalue reference type (expression may be lvalue or rvalue)
Even 5.2.7 of N3337:
dynamic_cast<T>(v)
If T is a pointer type, v shall be a prvalue of a pointer to complete class type, and the result is a prvalue of type T. If T is an lvalue reference type, v shall be an lvalue of a complete class type, and the result is an lvalue of the type referred to by T. If T is an rvalue reference type, v shall be an expression having a complete class type, and the result is an xvalue of the type referred to by T.
The only requirement there being that I use a complete class type, which std::move(p) is, isn't it?
Your code is, of course, fine:
If T is an rvalue reference type, v shall be an expression having a
complete class type, and the result is an xvalue of the type referred
to by T.
Presumably, dynamic_cast wasn't updated properly when rvalue references were introduced, and still enforces the pre-C++11 rule that rvalues shall only be bound to const lvalue references (note that it doesn't even work when altering the target type to B const&&, despite that being implied by the error message!).
Filed as #69390.
This seems to work instead:
B&& b = std::move(dynamic_cast<B&>(p));
Can't tell you why yours is incorrect.
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.