You’ll sometimes come across lvalues and rvalues maybe when trying to understand compiler error messages or when reading docs. A basic understanding will go a long way to making sense of them.
The names look strange, lvalue and rvalue. They started out, as I always understood them, to identify what things could go on the left side of an assignment and what things could go on the right side. If you declare an int variable called height and later assign it the value 2, then your code will say height is assigned 2. In this example, height appears to the left of the assignment operator and the number 2 integer literal is on the right. So height is an lvalue and 2 is an rvalue.
You can’t reverse these and try to say that the number 2 is assigned height. That doesn’t make any sense. And if you try to do something like this, your compiler is likely to give you an error message about how the number 2 cannot be an lvalue.
It turns out that there are actually two main properties that are important for variables and even other expression types. Does it have an identity? And is it moveable? What do these mean?
Well, an identity is anytime your application can refer to something by name, or through a pointer, or through a reference. If you can tell if something is different from something else or if it’s been changed in some way, then it has an identity.
Moveable is a bit harder to understand. Let’s say you have two integer variables, height and width, and you write in your code that height is assigned width. This means that after the assignment both height and width will have the same value and that value will be whatever the width was before the assignment. There’s another form of assignment though that can look just like this and that’s when the width gets moved into the height. When this happens, then height will be updated to contain the original value of the width and the width will be left in a valid but unspecified state.
Like most of my examples, I like to start out simple. Unfortunately, in this case the simple example can leave you wondering why? Why would the possibility that an int can be left in a valid but unspecified state even exist in the language. It didn’t always exist. This is sort of new. It makes more sense for a larger object such as a collection. Let’s say that you have a large collection of millions of ints that you need to transfer to another collection. You could do the traditional assignment where each int needs to get copied over to the new collection. That can take a lot of time. And if you never need to refer to the original collection ever again, then it’s a complete waste. What if you could just move the contents? You could then leave the original collection in an empty state which is perfectly valid. The language doesn’t tell you what to do with the original object so that’s why it’s unspecified. All the language says is that whatever you do, just make sure it’s valid. This is moveable.
Okay, back to the properties identifiable and moveable. Anytime you have two properties, you’ll have four possible ways they can be combined. One of the combinations in this case is just not needed though. That combination is whenever an object has no identity and cannot be moved. The language designers felt that this combination serves no purpose. So it’s gone.
That leaves the following three combinations:
- If it’s identifiable and not moveable, then this is the traditional, in-depth meaning of an lvalue.
- If it’s not identifiable but is moveable, then this is what’s called a pure rvalue. Or a prvalue.
- If it’s both identifiable and moveable, then all I can say here is you need to be careful. This is called an xvalue. And the language designers left the meaning of the letter x unspecified. Think of it as an expert value. If you’re dealing with xvalues, then you need to know what you’re doing.
What happened to rvalues? Well, rvalues are now defined to be more of a property. Rvalues map to the moveable property and include both xvalues and prvalues.
And the other property, identifiable, is now called general lvalues or glvalues and includes both lvalues and xvalues.