Why is this code compiling? I thought that rvalues returned by ctor are not located in memory and therefore can't be used as lvalues.
#include <iostream>
#include <vector>
class Y {
public :
explicit Y(size_t num = 0)
: m_resource {std::vector<int>(num)}
{
}
std::vector<int> m_resource;
};
int main(int argc, const char * argv[]) {
Y(1) = Y(0); // WHAT?!?
return 0;
}
An lvalue (locator value) represents an object that occupies some identifiable location in memory (i.e. has an address). rvalues are defined by exclusion. Every expression is either an lvalue or an rvalue, so, an rvalue is an expression that does not represent an object occupying some identifiable location in memory.
lvalues are “left values”: expressions that can be assigned to, which can be on the left of an assignment. rvalues are “right values”: everything else, which must be on the right of an assignment.
But where exactly is this rvalue stored? It's up to the compiler where to store a temporary; the standard only specifies its lifetime. Typically, it will be treated like an automatic variable, stored in registers or in the function's stack frame.
Variables are lvalues and hence they may appear on the left-hand side of an assignment. Numeric literals are rvalues and hence they may not be assigned and cannot appear on the left-hand side.
The synthesized assignment operator is declared as one of these (if it can be synthesized and isn't declared as deleted) according to see 12.8 [class.copy] paragraph 18:
Y& Y::operator=(Y const&)
Y& Y::operator=(Y&)
()That is, like for any other member function which isn't specifically declared with ref-qualifiers it is applicable to rvalues.
If you want to prevent a temporary object on the left hand side of the assignment you'd need to declare it correspondingly:
class Y {
public :
explicit Y(std::size_t num = 0);
Y& operator= (Y const&) & = default;
};
The standard uses the name ref-qualifier for the &
before the = default
. The relevant proposal is N2439. I don't know where there is a good description of ref-qualifiers. There is some information at this question.
Not sure where you got that specific rule of thumb. If any, a rule of thumb would be (from Scott Meyers): if it has a name, it's an lvalue.
In this case, you're creating a temporary object and passing it to an assignment method/function. There is no problem with that. In fact, it might even make sense to do so, as in
// Applies foo to a copy, not the original.
(Y(1) = y).foo()
It is true that Y(*)
don't have names here, though, and hence they are rvalues.
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