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.
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.
Should the following sample compile?
struct B;
struct A
{
A(B*&&){}
};
struct B : A
{
B() : A(this){}
};
int main(){}
On LWS with clang it compiles, but with gcc I get:
no known conversion for argument 1 from 'B* const' to 'B*&&'
and if I add a const it compiles.
I would like to also point out MSVC gets it wrong too:
cannot convert parameter 2 from 'B *const ' to 'B *&&'
so it looks like we have a bug in two compilers.
BUGS FILED
MSVC bug link
GCC bug link
Yes, that should compile.
It is incorrect to implement this as cv T* const (where cv is the cv-qualifiers for the function, if any, and T is the class type). this is not const, merely a prvalue expression of a built-in type (not modifiable).
Many people think that because you can't modify this it must be const, but as Johannes Schaub - litb once commented long ago, a much better explanation is something like this:
// by the compiler
#define this (__this + 0)
// where __this is the "real" value of this
Here it's clear that you can't modify this (say, this = nullptr), but also clear no const is necessary for such an explanation. (And the value you have in your constructor is just the value of the temporary.)
I say clang is right - the code should compile. For some reason, GCC considers the this pointer to be const despite the following:
The type of this in a member function of a class X is X*. If the member function is declared const, the type of this is const X*, if the member function is declared volatile, the type of this is volatile X*, and if the member function is declared const volatile, the type of this is const volatile X*.
So in this case, this should be a prvalue B* and perfectly bindable to B*&&. However, note that when binding this to an rvalue reference, the value of this will be copied into a temporary object and the reference will instead be bound to that. This ensures that you never actually modify the original this value.
A reference to type "cv1 T1" is initialized by an expression of type "cv2 T2" as follows:
[...]
[...] or the reference shall be an rvalue reference.
If the initializer expression
is an xvalue, class prvalue, array prvalue or function lvalue and [...], or
has a class type (i.e., T2 is a class type), [...]
then [...]
Otherwise, a temporary of type “cv1 T1” is created and initialized from the initializer expression using the rules for a non-reference copy-initialization (8.5). The reference is then bound to the temporary. [...]
The snippet below compiles
#include <iostream>
int& f() { static int i = 100; std::cout << i << '\n'; return i; }
int main()
{
int& r = f();
r = 101;
f();
}
and print the values (live example)
100
101
Now, reading §8.5.3/5 in N4140, I can see that it compiles because of bullet point (5.1.1), that is, the reference is an lvalue reference, the initializer expression is an lvalue and int is reference-compatible with int (or with int& - I don't know for sure which one I should use here).
Bullet points (5.1) and (5.1.1):
— If the reference is an lvalue reference and the initializer expression
— is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with
“cv2 T2,” or ...
Now suppose I change the left value reference in the declaration int& r = f(); by a right value reference, i.e., int&& r = f();. I know the code won't compile, as an rvalue reference doesn't bind to an lvalue. But what I'm curious is, how to reach this conclusion using the Standard?
I'll explain what are my difficulties:
Clearly int&& r = f(); is covered by bullet point (5.2), because the reference is an rvalue reference.
Bullet point (5.2):
— Otherwise, the reference shall be an lvalue reference to a
non-volatile const type (i.e., cv1 shall be const), or the reference
shall be an rvalue reference.
In principle, I would say that (5.2.1.1) supports this initialization as the initializer is a function lvalue and int is reference compatible with int (or with int&).
Bullet points (5.2.1) and (5.2.1.1):
— If the initializer expression
— is an xvalue (but not a bit-field), class prvalue, array prvalue or function
lvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or ...
Edit
I've included the bullet points verbatim from N4140 (C++14), which are equivalent to similar bullet points in N3337 (C++11).
the initializer expression is an lvalue and int is reference-compatible with int (or with int& - I don't know for sure which one I should use here).
Reference-compatibility is a relation applied to the type referred to, not the reference type. For example, [dcl.init.ref]/5 talks about intializing "a reference to type cv1 T1 by an expression of type cv2 T2", and later compares e.g. "where T1 is not reference-related to T2".
The type of the expression f() is just int, despite the fact that the return type of f is int&. Expressions simply do not have reference type when we observe them(*); the reference is stripped and used to determine the value category (see [expr]/5). For int& f(), the expression f() is an lvalue; for int g(), the expression g() is an rvalue.
(*)To be perfectly precise, expressions can have reference type in the Standard, but only as the "initial" resulting type. The reference is dropped "prior to any further analysis", which implies that this referencess is simply not observable through the type.
Now suppose I change the left value reference in the declaration int& r = f(); by a right value reference, i.e., int&& r = f();. I know the code won't compile, as an rvalue reference doesn't bind to an lvalue. But what I'm curious is, how to reach this conclusion using the Standard?
The confusion, as it seems from the discussion in the comments, seems to be that f() is not a function lvalue. Value categories such as "lvalue" and "rvalue" are properties of expressions. The term "function lvalue" must therefore refer to an expression, namely an expression of function type with the value category "lvalue".
But the expression f() is a function call expression. Grammatically, it's a postfix-expression, the postfix being the function argument list. As per [expr.call]/10:
A function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise.
And [expr.call]/3
If the postfix-expression designates a destructor [...]; otherwise, the type of the function call expression is the return type of the statically chosen function [...]
That is, the (observed see above) type of the expression f() is int, and the value category is "lvalue". Note that the (observed) type is not int&.
A function lvalue is for example an id-expression like f, the result of indirecting a function pointer, or an expression yielding any kind of reference to function:
using ft = void();
void f();
ft& l();
ft&& r();
ft* p();
// function lvalue expressions:
f
l()
r()
*p()
[expr.prim.general]/8 specifies that those identifiers like f are, as id-expressions, lvalues:
An identifier is an id-expression provided it has been suitably declared. [...] The type of the expression is the type of the identifier. The result is the entity denoted by the identifier. The result is an lvalue if the entity is a function, variable, or data member and a prvalue otherwise.
Back to the example int&& r = f();. Using some post-N4296 draft.
[dcl.init.ref]
5 A reference to type “cv1 T1” is initialized by an expression of type
“cv2 T2” as follows:
(5.1) If the reference is an lvalue reference and the initializer expression
The reference is an rvalue reference. 5.1 does not apply.
(5.2) Otherwise, the reference shall be an lvalue reference to a
non-volatile const type (i.e., cv1 shall be const), or the reference
shall be an rvalue reference. [example omitted]
This applies, the reference is an rvalue-reference.
(5.2.1) If the initializer expression
(5.2.1.1) is an xvalue (but not a bit-field), class prvalue, array prvalue or function lvalue and [...], or
(5.2.1.2) has a class type (i.e., T2 is a class type) [...]
The initializer is an lvalue of type int. 5.2.1 does not apply.
(5.2.2) Otherwise:
(5.2.2.1) If T1 or T2 is a class type [...]
(5.2.2.2) Otherwise, a temporary of type “cv1 T1” is created and copy-initialized (dcl.init) from the initializer expression. The reference is then bound to the temporary.
Finally, 5.2.2.2 applies. However:
If T1 is reference-related to T2:
(5.2.2.3) cv1 shall be the same cv-qualification as, or greater cv-qualification than, cv2; and
(5.2.2.4) if the reference is an rvalue reference, the initializer expression shall not be an lvalue.
T1 and T2 are int (the reference of the return type of f() is removed and used only to determine the value category), so they're reference-related. cv1 and cv2 are both empty. The reference is an rvalue reference, and f() is an lvalue, hence 5.2.2.4 renders the program ill-formed.
The reason why the term "function lvalue" appears in 5.2.1.1 might be related to the problem of "function rvalues" (see, for example, N3010 - Rvalue References as "Funny" Lvalues). There were no function rvalues in C++03, and it seems the committee didn't want to introduce them in C++11. Without rvalue references, I think it's impossible to get a function rvalue. For example, you may not cast to a function type, and you may not return function types from a function.
Probably for consistency, function lvalues can be bound to rvalue references to function types via a cast:
template<typename T>
void move_and_do(T& t)
{
T&& r = static_cast<T&&>(t); // as if moved
}
int i = 42;
move_and_do(i);
move_and_do(f);
But for T being a function type like void(), the value category of static_cast<T&&>(t) is lvalue (there are no rvalues of function type). Hence, rvalue references to function types can bind to function lvalues.
Should the following sample compile?
struct B;
struct A
{
A(B*&&){}
};
struct B : A
{
B() : A(this){}
};
int main(){}
On LWS with clang it compiles, but with gcc I get:
no known conversion for argument 1 from 'B* const' to 'B*&&'
and if I add a const it compiles.
I would like to also point out MSVC gets it wrong too:
cannot convert parameter 2 from 'B *const ' to 'B *&&'
so it looks like we have a bug in two compilers.
BUGS FILED
MSVC bug link
GCC bug link
Yes, that should compile.
It is incorrect to implement this as cv T* const (where cv is the cv-qualifiers for the function, if any, and T is the class type). this is not const, merely a prvalue expression of a built-in type (not modifiable).
Many people think that because you can't modify this it must be const, but as Johannes Schaub - litb once commented long ago, a much better explanation is something like this:
// by the compiler
#define this (__this + 0)
// where __this is the "real" value of this
Here it's clear that you can't modify this (say, this = nullptr), but also clear no const is necessary for such an explanation. (And the value you have in your constructor is just the value of the temporary.)
I say clang is right - the code should compile. For some reason, GCC considers the this pointer to be const despite the following:
The type of this in a member function of a class X is X*. If the member function is declared const, the type of this is const X*, if the member function is declared volatile, the type of this is volatile X*, and if the member function is declared const volatile, the type of this is const volatile X*.
So in this case, this should be a prvalue B* and perfectly bindable to B*&&. However, note that when binding this to an rvalue reference, the value of this will be copied into a temporary object and the reference will instead be bound to that. This ensures that you never actually modify the original this value.
A reference to type "cv1 T1" is initialized by an expression of type "cv2 T2" as follows:
[...]
[...] or the reference shall be an rvalue reference.
If the initializer expression
is an xvalue, class prvalue, array prvalue or function lvalue and [...], or
has a class type (i.e., T2 is a class type), [...]
then [...]
Otherwise, a temporary of type “cv1 T1” is created and initialized from the initializer expression using the rules for a non-reference copy-initialization (8.5). The reference is then bound to the temporary. [...]
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.