Re: rvalues and lvalues
On 2011-12-07 23:40, Frank Birbacher wrote:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hi!
Am 05.11.11 00:37, schrieb Andrzej Krzemie??ski:
vector<int> & operator<< ( vector<int> & x, int y ){
return x.push_back(y), x;
}
vector<int> operator<< ( vector<int> && x, int y ){
return x<< y;
}
This is the part where I'm still lost: it is hard to see why the latter
function does not call itself recursively.
I understand:
The second operator is called for unnamed values as first parameter
(temporaries).
More precisely: It is called, when the argument is a (mutable) rvalue. I
emphasize this, because the overload selection/reference binding rules
are based on *value categories*.
Such a value is bound to the& of the first operator and
the object is filled with a value. This is why the second operator must
not return a reference&. It returns by value instead.
Questions:
1. Why does the latter not call itself?
Because x is an lvalue! Yes, this seems astonishing at first, when you
compare this view with the view of the "external caller". It is much
easier to get used to it, when you realize that not alone the type
determines whether something is an lvalue or an rvalue. This means that
just having an rvalue reference of something does mean that the
corresponding expression is an rvalue.
This in some aspects astonishing consequence has indeed the following
advantages:
a) Whether an expression is an lvalue or an rvalue is more or less
determined whether you have a *named* variable or not (Note: Variables
include references in C++11), so there *is* some pattern here.
b) It helps programmers to recognize when a potentially mutable
operation will happen when working with a variable, it helps to prevent
those traps that you could run into with types like std::auto_ptr!
Consider:
void foo(std::vector&& x) {
std::vector y = x; // Assume this would move the contents
// of x into y...
if (x.size() > 2) { .. } // x is used as if it where unchanged..
};
This is the typical "auto_ptr-gotcha" problem! The rules are defined
such that x is an lvalue in the construction of y to prevent subtle
errors to occur. It is unclear what was intended here. To make that
clearer, user call should explicitly call std::move (or any moral
equivalent) to make clear what they want:
void foo(std::vector&& x) {
std::vector y = std::move(x); // Yes, I want to modify x!
if (x.size() > 2) { .. } // Oops, I must have overlooked
// something here...
};
2. Should the latter one read:
return std::move(x<< y);
?
Yes, this is correct. The expression "x << y" is an lvalue and applying
std::move to it in this case ensures move semantics for the return
statement.
HTH & Greetings from Bremen,
Daniel Kr??gler
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]