This question already has answers here:
Why not non-const reference to temporary objects? [duplicate]
(4 answers)
Closed 6 months ago.
For some reason I didn't manage to find this exact question. Why is it allowed to bind an rvalue to const lvalue reference, although it is impossible to to the same without the const?
I do understand that the lifetime of the rvalue gets an extension somehow (in the first case) , but if so, why would the compiler disallow changing that 'rvalue', which is not really a temporary object anymore.
For example, consider the following code:
int main(){
int &i=3; //produces error
const int&j =3; //compiles
return 1;
}
You may find the following article useful:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1993/N0345.pdf
I might be entirely wrong here, but this is how I rationalise it. An rvalue is constant, it cannot be changed. you cannot change the integer 5, fact. So when you bind the references the lvalue will have to be const. Otherwise your compiler will throw an error:
obj & a1 = bar();
invalid initialization of non-const reference of type ‘obj&’ from an rvalue of type ‘obj’
using g++
The only way to safely bind an rvalue to an lvalue is either by marking the lvalue as const, or using a mutable rvalue reference && (introduced in C++11 believe?)
struct obj {
};
obj bar() {
obj x;
return x;
}
int main() {
const obj & a1 = bar();
obj && a2 = bar();
return 0;
};
If you're asking about
void f(const int&);
f(1);
versus
void g(int&);
g(1);
the answer is to think about what happens if g looks like this:
void g(int& r) {
++r;
}
The name r-value comes from the right-value: which (roughly) means things that are on the right side of a x=y kind of statement. It is like: it can be read from, but it may not be suitable to be written on. For example a numeric literal can be on the right side (a=3) but it does not make sense to be on the left side (3=a).
With this reasoning, it looks logical to me why it is allowed to make const l-value references to r-values but not allowed to make non-const references.
Related
This question already has answers here:
Reference a temporary in msvc
(2 answers)
Closed last year.
I observe a different behavior for the compiler with the 2 following snippets of code:
With the first one, all seems OK to me with fundamental type like int:
void fi1(int& a) {}
void fi2(int&& a) {}
void func1() {
fi1(2+2); // Do not compile: normal since '2+2' is temporary and is not a variable
int a = 5;
fi2(a); // Do not compile: normal since 'a' is an lvalue
}
But I do not understand the second one with object type:
void fs1(string& a) {}
void fs2(string&& a) {}
void func2() {
fs1(string{""}); // Compile: but weird for me since there is no variable to reference !
fs2(string{""}); // Compile: normal since 'string{""}' is temporary and is not a variable
}
In the following post:
Understanding lvalue/rvalue expression vs object type
it is explained that an lvalue is when you can take its address. For sure, string{""} shall have an address somewhere in memory. But for me a key-point with rvalue is that you can't use it afterwards, making 'move semantic' possible.
When you are in fs2 function, the argument is an rvalue and you can do whatever you want without any side effect. In fs1, you expect that some variable elsewhere will have some impact if you modify it. fs2 should only be called with variable argument.
And why is working as expected with int and not with object ?
This strange behavior is due to the usage of MSVC.
This question already has answers here:
Why not non-const reference to temporary objects? [duplicate]
(4 answers)
Closed 3 years ago.
In C++ it is possible to pass an rvalue to a function accepting a const lvalue reference. I don't understand why this is.
The expression Function(Entity()) is ostensibly an rvalue, it has no identifiable location in memory. If Function() were to take an Entity& rather than a const Entity&, then this would not work. But if the function takes a const lvalue reference rather than an lvalue reference, then this works fine.
Could someone please provide an explanation for how this works and why?
class Entity
{
Entity()
{
std::cout << "Entity created\n";
}
}
void Function(const Entity& e) // recieved as const lvalue reference
{
std::cout "Inside function\n";
}
int main()
{
Function(Entity()); // passing rvalue
}
It's important to note is that this does not require classes. void Function(int const& i) { std::cout << i; } can be called as Function(2+2).
The reason is the same for classes and built-in types alike. A temporary can bind to const references. This is very intentional. It's safe and useful. Safe: Since you can't change the referenced object through a const reference, there is no chance of such a change getting lost in a temporary object. Useful: it saves the compiler from making copies of big objects.
This question already has answers here:
Why not non-const reference to temporary objects? [duplicate]
(4 answers)
Closed 6 years ago.
Lets say I have class A:
class A {
int i;
public:
A(){};
A(int i){this->i=i;};
};
And a simple test function:
void test(const A &a){...}
Now, if I do:
int main()
{
test(2);
}
It compiles and it will call the A(int i) constructor. But when I change the argument to be non-const: void test(A &a) I get a compilation error.
What is the difference between those cases, why the first one is allowed and the second not, and what actually happens in the initialization of the first case?
The difference between the two cases is that C++ compiler is allowed to create temporary objects to be passed to functions accepting a const references, but functions accepting non-const references must be provided with actual objects.
When you call test(2) what actually happens is this:
A hidden(2);
test(hidden);
The compiler creates hidden object, initializes it with 2, and passes the result to test. Since test is guaranteed to not modify A, this is fine.
When test does not provide such guarantee: imagine test that sets a new value:
void test(A& a) {
a.i++; // let's pretend "i" is public
}
If you call test with an actual object, i.e. A a(3); test(a); you can harvest the result of the update from a after test return. Calling test(2), on the other hand, gives you no way to access the result of an update. This indicates a potential error in the logic, so the compiler treats it as an error.
This is an interesting case. using const you can bind a reference to an rvalue. A more simpler example is below.
int get_num()
{
return 2;
}
int main(){
int& p = get_num(); // This is a compiler error. Can't create a non-const reference to an rvalue
const int& q = get_num(); // this will work. Can create const reference to an rvalue
}
This is part of the c++ standard.
what actually happens in the initialization of the first case?
In the 1st case, a temporary A will be constructed implicitly by A::A(int) and then bound to the lvalue reference to const. The effect of test(2); is same as test(A(2));.
Temporary can't be bound to lvalue reference to non-const, that's why the 2nd case failed.
I have the given code, which gets an error:
error: invalid initialization of non-const reference of type 'int&'
from an rvalue of type 'int' const int b = f(a++);
^
int f(int& a)
{
return a;
}
int main() {
// your code goes here
int a = 5;
int b = f(a++);
std::cout << b << std::endl;
return 0;
}
What the cause of this error ?
You can't bind a temporary to a non-const reference.
Post-increment (a++) increments a and returns a temporary with a's old value.
Why are you passing by non-const reference? - it doesn't look like you're changing the parameter inside the function, just just pass by value or const reference.
If you were changing the parameter, what would you expect the behavior to be, considering a++ already changes it? Would the change be intuitive? Legal?
The postfix increment operator on an int returns a temporary value. A temporary value cannot bind to a non-const lvalue reference, because modifying that temporary doesn't make sense. You are trying to bind the temporary to an int&, which is giving an error.
To fix this, either use the pre-increment operator (++a), or take your argument by value (it's better to pass builtin types as value rather than const T&):
int f(int a)
{
return a;
}
This function:
int f(int& a)
accepts non-const reference. Such references must always point to a valid objects, residing at certain memory locations (*).
Post incrementation works as follows:
- save current value as `r`
- increment original variable
- return `r`
That's because result of post-incrementation is a temporary, yielding value from before incrementation. Such temporary must be passed either as value or const reference:
int f(int a) //This will work
int f(const int& a) //And this
(*) In fact, older compilers allowed for such constrcuts. For example, this code will compile under VC6:
struct T {};
void f(T& t)
{
}
int main()
{
f(T());
}
However, such behaviour is not standard-compliant.
The code below results in Undefined Behaviour. Be sure to read ALL the answers for completeness.
When chaining an object via the operator<< I want to preserve the lvalue-ness / rvalue-ness of the object:
class Avenger {
public:
Avenger& operator<<(int) & {
return *this;
}
Avenger&& operator<<(int) && {
return *this; // compiler error cannot bind lvalue to rvalue
return std::move(*this);
}
};
void doJustice(const Avenger &) {};
void doJustice(Avenger &&) {};
int main() {
Avenger a;
doJustice(a << 24); // parameter should be Avenger&
doJustice(Avenger{} << 24); // parameter should be Avenger&&
return 0;
}
I cannot simply return *this which implies that the type of *this of an rvalue object is still an lvalue reference. I would have expected to be an rvalue reference.
Is it correct / recommended to return std::move(*this) on an member overloaded for the && qualifier, or should other method be used? I know that std::move is just a cast, so I think it’s ok, I just want to double check.
What is the reason/explanation that *this of an rvalue is an lvalue reference and not an rvalue reference?
I remember seeing in C++14 something about move semantics of *this. Is that related to this? Will any of the above change in C++14?
The type of this depends on the cv-qualifier of the member function: Avenger* or const Avenger*
But not on its ref-qualifier. The ref-qualifier is used only to determine the function to be called.
Thus, the type of *this is Avenger& or const Avenger&, no matter if you use the && or not. The difference is that the overload with && will be used then the called object is a r-value, while the & will not.
Note that rvalue-ness is a property of the expression, not the object. For example:
void foo(Avenger &x)
{
foo(x); //recursive call
}
void foo(Avenger &&x)
{
foo(x); //calls foo(Avenger &)!
}
That is, although in the second foo(), x is defined as an r-value reference, any use of the expression x is still an l-value. The same is true for *this.
So, if you want to move out the object, return std::move(*this) is The Right Way.
Could things have been different had this been defined as a reference value instead of as a pointer? I'm not sure, but I think that considering *this as an r-value could lead to some insane situations...
I didn't hear of anything changing about this in C++14, but I may be mistaken...
std::move is perhaps better called rvalue_cast.
But it is not called that. Despite its name, it is nothing but an rvalue cast: std::move does not move.
All named values are lvalues, as are all pointer dereferences, so using std::move or std::forward (aka conditional rvalue cast) to turn a named value that is an rvalue reference at point of declaration (or other reasons) into an rvalue at a particular point is kosher.
Note, however, that you rarely want to return an rvalue reference. If your type is cheap to move, you usually want to return a literal. Doing so uses the same std::move in the method body, but now it actually triggers moving into the return value. And now if you capture the return value in a reference (say auto&& foo = expression;), reference lifetime extension works properly. About the only good time to return an rvalue reference is in an rvalue cast: which sort of makes the fact that move is an rvalue cast somewhat academic.
This answer is in response to bolov's comment to me under his answer to his question.
#include <iostream>
class Avenger
{
bool constructed_ = true;
public:
Avenger() = default;
~Avenger()
{
constructed_ = false;
}
Avenger(Avenger const&) = default;
Avenger& operator=(Avenger const&) = default;
Avenger(Avenger&&) = default;
Avenger& operator=(Avenger&&) = default;
Avenger& operator<<(int) &
{
return *this;
}
Avenger&& operator<<(int) &&
{
return std::move(*this);
}
bool alive() const {return constructed_;}
};
void
doJustice(const Avenger& a)
{
std::cout << "doJustice(const Avenger& a): " << a.alive() << '\n';
};
void
doJustice(Avenger&& a)
{
std::cout << "doJustice(Avenger&& a): " << a.alive() << '\n';
};
int main()
{
Avenger a;
doJustice(a << 24); // parameter should be Avenger&
doJustice(Avenger{} << 24); // <--- this one
// Avenger&& dangling = Avenger{} << 24;
// doJustice(std::move(dangling));
}
This will portably output:
doJustice(const Avenger& a): 1
doJustice(Avenger&& a): 1
What the above output demonstrates is that a temporary Avenger object will not be destructed until the sequence point demarcated by the ';' just before the comment "// <--- this one" above.
I've removed all undefined behavior from this program. This is a fully conforming and portable program.
It is NOT ok to return std::move(*this) on a member overloaded for the && qualifier. The problem here is not with the std::move(*this) (which other answers correctly show that it is ok) but with the return type. The problem is very subtle and it was almost done by the c++11 standardization committee. It is explained by Stephan T. Lavavej in his presentation Don’t Help the Compiler during Going Native 2013. His example can be found at around minute 42 in the linked video. His example is slightly different and doesn’t involve *this and uses overload by parameter reference type rather than by method ref qualifiers but the principle is still the same.
So what is wrong with the code?
Short introduction: a reference bound to a temporary object prolongs the lifetime of the temporary object for the lifetime of the reference. That is what makes code like this be ok:
void foo(std::string const & s) {
//
}
foo("Temporary std::string object constructed from this char * C-string");
The important part here is that this property is not transitive, meaning that for the reference to prolong the lifetime of the temporary object, it must bind directly to the temporary object, and not to a reference to it.
Returning to my example:
For completness let’s add a function that takes only a const lvalue reference to Avenger (no rvalue reference overload):
void doInjustice(Avenger const &) {};
the next two calls result in UB if referencing the parameter inside the functions:
doInjustice(Avenger{} << 24); // calls `void doInustice(Avenger const &) {};`
doJustice(Avenger{} << 24); // calls `void doJustice(Avenger &&) {};`
The temporary objects constructed at parameter evaluation are destroyed as soon as the function are called for the reasons exposed above and the parameters are dangling references. Referencing them inside the functions will result in UB.
The correct way is to return by value:
class Avenger {
public:
Avenger& operator<<(int) & {
return *this;
}
Avenger operator<<(int) && {
return std::move(*this);
}
};
A copy is still eluded with the move semantics, and the return is a temporary, meaning that it will call the correct overload, but we avoid this subtle but nasty silent bug.
The example of Stephan T. Lavavej: Don’t Help the Compiler (42m–45m)
string&& join(string&& rv, const char * ptr) {
return move(rv.append(", ").append(ptr));
}
string meow() { return "meow"; }
const string& r = join(meow(), "purr");
// r refers to a destroyed temporary!
//Fix:
string join(string&& rv, const char * ptr) {
return move(rv.append(", ").append(ptr));
}
Posts on SO explaining the prolonging of life of temporary objects through references:
Does a const reference prolong the life of a temporary?
Initializing a reference
My git copy of the C++ Standard (page 290, section15.2, point 6) says:
"The exceptions to this lifetime rule [of a reference binding to a
temporary extending the lifetime of that temporary] are:
sub 9 - A temporary object bound to a reference parameter in a
function call persists until the completion of the
full-expression containing the call.
sub 10 - The lifetime of a temporary bound to the returned value
in a function return statement (9.6.3) is not extended;
the temporary is destroyed at the end of the
full-expression in the return statement. [...]"
Therefore, the implicit this `parameter' passed to
Avenger&& operator<<(int) &&
{
return std::move(*this);
}
ends its life at the ; of the statement that calls operator<< on a temporary object, even if a reference is bound to it. So this fails:
Avenger &&dangler = Avenger{} << 24; // destructor already called
dangler << 1; // Operator called on dangling reference
If OTOH the return is by value:
Avenger operator<<(int) &&
{
return std::move(*this);
}
then none of that misery happens, and there is often no extra cost in terms of copying. And indeed, if you don't offer the compiler to create that return value by move-constructing from *this, then it 'll make a copy.
So snatch *this from the brink of oblivion, and have the best of both worlds.