The code is as follows:
#include <iostream>
using namespace std;
class A {
};
A rtByValue() {
return A();
}
void passByRef(A &aRef) {
// do nothing
}
int main() {
A aa;
rtByValue() = aa; // compile without errors
passByRef(rtByValue()); // compile with error
return 0;
}
The g++ compiler gives the following error:
d.cpp: In function ‘int main()’:
d.cpp:19:23: error: invalid initialization of non-const reference of type ‘A&’ from an rvalue of type ‘A’
d.cpp:12:6: error: in passing argument 1 of ‘void passByRef(A&)’
It says that I can't pass an rvalue as an argument of a non-const reference, but what I'm confused about is why I can assign to this rvalue, just as the code shows.
Passing the rvalue rtByValue() to a function that expects an lvalue reference doesn't work because this would require the lvalue reference argument to be initialized from an rvalue. §8.5.3/5 describes how lvalue references can be initialized – I won't quote it in full, but it basically says that an lvalue reference can be initialized
Since the argument we need to initialize is not a const-reference, none of this applies.
On the other hand,
rtByValue() = aa;
i.e., assigning to a temporary object, is possible because of:
(§3.10/5) An lvalue for an object is necessary in order to modify the object except that an rvalue of class type can also be used to modify its referent under certain circumstances. [ Example: a member function called for an object (9.3) can modify the object. — end example ]
So this works only because A is of class-type, and the (implicitly defined) assignment operator is a member function. (See this related question for further details.)
(So, if rtByValue() were to return, for example, an int, then the assignment wouldn't work.)
Because you can (but shouldn't!) override operator= such that calling it on an rvalue makes sense. Consider the following code:
#include<iostream>
using namespace std;
class foo;
foo* gotAssigned = NULL;
int assignedto = -1;
class foo {
public:
foo(int v) : val(v) {}
foo& operator=(int v) {
assignedto=v;
gotAssigned = this;
val = v;
return *this;
}
int val;
};
foo theFoo(2);
foo returnTheFooByValue() {
return theFoo;
}
main() {
returnTheFooByValue()=5;
cout << "[" << assignedto << "] " << theFoo.val << " versus " << gotAssigned->val << endl;
}
Now let's compile it a few ways:
$ g++ -O0 -o rveq rveq.cc && ./rveq
[5] 2 versus 5
$ g++ -O1 -o rveq rveq.cc && ./rveq
[5] 2 versus 2
$ g++ -O4 -o rveq rveq.cc && ./rveq
[5] 2 versus -1218482176
I can't promise you'll see the same results.
As you can see, the assignment happens, but any attempt to use the object that got assigned results in implementation-specific behaviour.
Incidentaly, this only applies to user-defined types. This code:
int v(){
return 2;
}
main(){
v()=4;
}
doesn't compile.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With