Like it!

Join us on Facebook!

Like it!

Understanding the meaning of lvalues and rvalues in C++

A lightweight introduction to a couple of basic C++ features that act as a foundation for bigger structures.

I have been struggling with the concepts of lvalue and rvalue in C++ since forever. I think that now is the right time to understand them for good, as they are getting more and more important with the evolution of the language.

Once the meaning of lvalues and rvalues is grasped, you can dive deeper into advanced C++ features like move semantics and rvalue references (more on that in future articles).

Lvalues and rvalues: a friendly definition

First of all, let's keep our heads away from any formal definition. In C++ an lvalue is something that points to a specific memory location. On the other hand, a rvalue is something that doesn't point anywhere. In general, rvalues are temporary and short lived, while lvalues live a longer life since they exist as variables. It's also fun to think of lvalues as containers and rvalues as things contained in the containers. Without a container, they would expire.

Let me show you some examples right away.

int x = 666;   // ok

Here 666 is an rvalue; a number (technically a literal constant) has no specific memory address, except for some temporary register while the program is running. That number is assigned to x, which is a variable. A variable has a specific memory location, so its an lvalue. C++ states that an assignment requires an lvalue as its left operand: this is perfectly legal.

Then with x, which is an lvalue, you can do stuff like that:

int* y = &x;   // ok

Here I'm grabbing the the memory address of x and putting it into y, through the address-of operator &. It takes an lvalue argument and produces an rvalue. This is another perfectly legal operation: on the left side of the assignment we have an lvalue (a variable), on the right side an rvalue produced by the address-of operator.

However, I can't do the following:

int y;
666 = y; // error!

Yeah, that's obvious. But the technical reason is that 666, being a literal constant — so an rvalue, doesn't have a specific memory location. I am assigning y to nowhere.

This is what GCC tells me if I run the program above:

error: lvalue required as left operand of assignment

He is damn right; the left operand of an assigment always require an lvalue, and in my program I'm using an rvalue (666).

I can't do that either:

int* y = &666; // error!

GCC says:

error: lvalue required as unary '&' operand`

He is right again. The & operator wants an lvalue in input, because only an lvalue has an address that & can process.

Functions returning lvalues and rvalues

We know that the left operand of an assigment must be an lvalue. Hence a function like the following one will surely throw the lvalue required as left operand of assignment error:

int setValue()
    return 6;

// ... somewhere in main() ...

setValue() = 3; // error!

Crystal clear: setValue() returns an rvalue (the temporary number 6), which cannot be a left operand of assignment. Now, what happens if a function returns an lvalue instead? Look closely at the following snippet:

int global = 100;

int& setGlobal()
    return global;    

// ... somewhere in main() ...

setGlobal() = 400; // OK

It works because here setGlobal returns a reference, unlike setValue() above. A reference is something that points to an existing memory location (the global variable) thus is an lvalue, so it can be assigned to. Watch out for & here: it's not the address-of operator, it defines the type of what's returned (a reference).

The ability to return lvalues from functions looks pretty obscure, yet it is useful when you are doing advanced stuff like implementing some overloaded operators. More on that in future chapters.

Lvalue to rvalue conversion

An lvalue may get converted to an rvalue: that's something perfectly legit and it happens quite often. Let's think of the addition + operator for example. According to the C++ specifications, it takes two rvalues as arguments and returns an rvalue.

Let's look at the following snippet:

int x = 1;
int y = 3;
int z = x + y;   // ok

Wait a minute: x and y are lvalues, but the addition operator wants rvalues: how come? The answer is quite simple: x and y have undergone an implicit lvalue-to-rvalue conversion. Many other operators perform such conversion — subtraction, addition and division to name a few.

Lvalue references

What about the opposite? Can an rvalue be converted to lvalue? Nope. It's not a technical limitation, though: it's the programming language that has been designed that way.

In C++, when you do stuff like

int y = 10;
int& yref = y;
yref++;        // y is now 11

you are declarying yref as of type int&: a reference to y. It's called an lvalue reference. Now you can happily change the value of y through its reference yref.

We know that a reference must point to an existing object in a specific memory location, i.e. an lvalue. Here y indeed exists, so the code runs flawlessly.

Now, what if I shortcut the whole thing and try to assign 10 directly to my reference, without the object that holds it?

int& yref = 10;  // will it work?

On the right side we have a temporary thing, an rvalue that needs to be stored somewhere in an lvalue.

On the left side we have the reference (an lvalue) that should point to an existing object. But being 10 a numeric constant, i.e. without a specific memory address, i.e. an rvalue, the expression clashes with the very spirit of the reference.

If you think about it, that's the forbidden conversion from rvalue to lvalue. A volatile numeric constant (rvalue) should become an lvalue in order to be referenced to. If that would be allowed, you could alter the value of the numeric constant through its reference. Pretty meaningless, isn't it? Most importantly, what would the reference point to once the numeric value is gone?

The following snippet will fail for the very same reason:

void fnc(int& x)

int main()
    fnc(10);  // Nope!
    // This works instead:
    // int x = 10;
    // fnc(x);

I'm passing a temporary rvalue (10) to a function that takes a reference as argument. Invalid rvalue to lvalue conversion. There's a workaround: create a temporary variable where to store the rvalue and then pass it to the function (as in the commented out code). Quite inconvenient when you just want to pass a number to a function, isn't it?

Const lvalue reference to the rescue

That's what GCC would say about the last two code snippets:

error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'

GCC complains about the reference not being const, namely a constant. According to the language specifications, you are allowed to bind a const lvalue to an rvalue. So the following snippet works like a charm:

const int& ref = 10;  // OK!

And of course also the following one:

void fnc(const int& x)

int main()
    fnc(10);  // OK!

The idea behind is quite straightforward. The literal constant 10 is volatile and would expire in no time, so a reference to it is just meaningless. Let's make the reference itself a constant instead, so that the value it points to can't be modified. Now the problem of modifying an rvalue is solved for good. Again, that's not a technical limitation but a choice made by the C++ folks to avoid silly troubles.

This makes possible the very common C++ idiom of accepting values by constant references into functions, as I did in the previous snipped above, which avoids unnecessary copying and construction of temporary objects.

Under the hood the compiler creates an hidden variable for you (i.e. an lvalue) where to store the original literal constant, and then bounds that hidden variable to your reference. That's basically the same thing I did manually in a couple of snippets above. For example:

// the following...
const int& ref = 10;

// ... would translate to:
int __internal_unique_name = 10;
const int& ref = __internal_unique_name;

Now your reference points to something that exists for real (until it goes out of scope) and you can use it as usual, except for modifying the value it points to:

const int& ref = 10;
std::cout << ref << "\n";   // OK!
std::cout << ++ref << "\n"; // error: increment of read-only reference ‘ref’


Understanding the meaning of lvalues and rvalues has given me the chance to figure out several of the C++'s inner workings. C++11 pushes the limits of rvalues even further, by introducing the concept of rvalue references and move semantics, where — surprise! — rvalues too are modifiable. I will restlessly dive into that minefield in one of my next articles.


Thomas Becker's Homepage - C++ Rvalue References Explained (link)
Eli Bendersky's website - Understanding lvalues and rvalues in C and C++ (link)
StackOverflow - Rvalue Reference is Treated as an Lvalue? (link)
StackOverflow - Const reference and lvalue (link) - Reference declaration (link)

Ru on March 11, 2018 at 00:57
Thank you so much for the explanation.
Alfredo from Mexico on April 22, 2018 at 01:32
Thank you for the explanation!
Rajeev from bangalore on November 20, 2018 at 13:10
Superb explanation, simple neat and clean way to explain , good going
Sameer on November 20, 2018 at 16:55
Such a beautiful explanation!
Thang Bui on December 24, 2018 at 05:33
Thank you.
Raj on February 14, 2019 at 05:55
Thank you. on March 18, 2019 at 06:38
basic but clearly introduction, it help me to review those knowledge i had ten years ago:)
TT on April 17, 2019 at 21:15
Very nice and clear explanation! Thank you.
anastasiia_kos on April 29, 2019 at 10:39
Thanks for such a clear and concise explanation of this topic!
Andong Zhan on May 04, 2019 at 20:00
Thanks for the explanation!
Anil Patil on May 05, 2019 at 19:40
Very clear explanation.
Hareesh Vutla on May 10, 2019 at 02:06
Thanks for a very nice explanation
Camilo Jiménez on May 10, 2019 at 03:12
Thanks, a Little bit longer than i usually find a answer, but it's super clear.
Arsin on May 30, 2019 at 21:34
Many thanks. I learned a lot about the rvalues references!
Wubin on June 03, 2019 at 22:20
Great answer!
James on June 11, 2019 at 02:26
Thanks for this post! Super easy to read, informative, and exactly what I was looking for.
Mash on June 12, 2019 at 23:33
Well presented!!
Arun on June 21, 2019 at 20:19
Thank you for this. I attempted to understand this several times but never gave it the attention it deserved. Thanks for the simplicity and the references, appreciate it.
Sean on June 29, 2019 at 05:28
Good explanation!
Prasad Sardeshmukh on July 23, 2019 at 18:14
Thanks a lot for the details.
Youssef on July 27, 2019 at 21:07
Thank you for the clear and easy to follow explanation.
Shivkumar on August 07, 2019 at 15:31
Thank you!
Kyle on August 07, 2019 at 19:36
Very well written. Thank you so much!
Chau on August 09, 2019 at 09:34
Thank you so much. It's so crystal clear
guo on August 10, 2019 at 07:57
> Can an rvalue be converted to lvalue? Nope.

What about:
> int&& value = 123; ?
Hindounette on August 12, 2019 at 16:54
Thank you for the explanations ! Also thank you for making it crystal clear ! It is indeed easy to understand !
Sandy on August 13, 2019 at 06:56
No one could explain lvalue/rvalue simpler than this. Thank you bro!
Triangles on August 14, 2019 at 12:22
@guo that's a rvalue reference. I wrote about it here:
Swati on August 21, 2019 at 08:09
Thank you so much for the amazing article. Very nicely explained. Would like to read more of your articles! :)
Kusuma K V on August 25, 2019 at 08:36
Thank You for a very clear and easy to understand kind of explanation of the internal working. It helped a lot.
atong on August 30, 2019 at 20:09
thank you!
Muhammad Farhan on September 07, 2019 at 12:39
Indeed you gave us an amazing insight of C++ by explaining these l and r values. Thank you !!
Ying on October 11, 2019 at 18:45
Nice, clear, simple.
James on October 16, 2019 at 08:36
Thank you for the explanation, it is very helpful!
Rathinavelu ( on November 03, 2019 at 12:26
C++ has a lot more Sir, for example glvalue, prvalue, xrvalue in addition to rvalue and lvalue.
Maloy on November 05, 2019 at 22:01
Thank you for this explanation. Very helpful.
Anurag on November 10, 2019 at 17:05
Very simple and insightful explanation
David on November 12, 2019 at 15:40
you said "firts of all" instead of "First of all"
Triangles on November 13, 2019 at 11:42
@David typo fixed. Thank you!
Gan on November 19, 2019 at 09:43
As a beginner of C++, I think this is very helpful.
blackBeard on November 22, 2019 at 01:03
WOW, such great explanation
lvalueBRO on November 29, 2019 at 11:09
Hey man that was a crystal clear explanation.. didn't got this superb explanation anywhere in the books and teachers .. love it
gak on December 06, 2019 at 15:28
Great Explanation!
rValueVoodoo on December 17, 2019 at 10:56
Great Article!
eman on December 24, 2019 at 07:38
Thank you for this article with high readability!
Manish Sogi on December 25, 2019 at 05:26
Great Content!
Bharanidharan Subramani on December 26, 2019 at 10:41
Very Excellent explanations. Great Work. Thanks for making understanding the concepts in clear crystal way. Your explanation are going like a nail in tree.
Bharat on January 07, 2020 at 10:16
Excellent explanation!!
mngl on January 16, 2020 at 21:22
Amisha patel on January 21, 2020 at 17:04
An excellent and concise as well discrete explanation !!
thanks allot...
Amisha patel on January 21, 2020 at 17:19
Zhen Wang on January 24, 2020 at 08:22
An excellent explanation. Just one suggestion that it's better to say prvalue instead of rvalue.
Mousumi on January 27, 2020 at 09:09
Excellent explanation with example
Tanya Kejriwal on January 27, 2020 at 22:11
Very nice and simple explanation. The flow of the article made it so much easier to understand. The code snippets helped too. Thanks!
Ill on January 30, 2020 at 23:49
Wow I've been coding in C++ for so many years and I still get confused by this sometimes. The last part about const& helped me understand why I was getting a compiler error in code I knew should compile. Duh, make my function argument be const& instead of & like I usually do.
alok gupta on February 07, 2020 at 07:45
thanks...great explaination
Sona on February 12, 2020 at 19:46
Excellent explanation
Sona on February 12, 2020 at 19:47
Excellent explanation
Tom Sherlock on February 16, 2020 at 18:14
Could we then say that
lvalue is the contained value and
rvalue is the uncontained value?
An Mai on February 19, 2020 at 00:49
This is excellent! Thank you so much!
Dragan on February 24, 2020 at 00:03
Nice and clear
Triangles on February 26, 2020 at 11:45
@Tom Sherlock why not! and thanks for catching the typos :)
Rénald on March 16, 2020 at 01:59
Thanks a lot, and btw I learnt more about the use of const keyword for function parameters ;)
newraozx on April 01, 2020 at 14:35
The Best expiation I have got ever.... thank you for this
Osama Mhiri on April 02, 2020 at 18:46
thanks a lot, you are great !
junaid on April 02, 2020 at 20:03
very good explaination
Aman Prajapati on April 19, 2020 at 05:41
Very nice explanation!!!!
D on April 19, 2020 at 11:47
great explanation !!!
epic on April 19, 2020 at 21:27
Very clear explanation, keep up the good work!
Vincent on April 30, 2020 at 03:01
Absolutely brilliant! So clear, precise and concise. Thank you!
Sami on May 02, 2020 at 04:43
thank you so much. I appreciate it. Very well-explained!
FiLL on May 05, 2020 at 17:37
Thank you for explanation
biggalicious on May 06, 2020 at 01:20
Awesome!!!!!!!!!!! More please. Greatly appreciated i would be yessir
H on May 16, 2020 at 17:31
perfectly explained, thanks a bunch :)
ibra on May 22, 2020 at 04:55
great contribute to the community, this is the way how the whole humanity grows, congrats!!
Tobi on May 29, 2020 at 10:49
Great writeup.
Please, do for xvalue, glvalue, prvalue
Fubini on June 04, 2020 at 16:31
Thank you from Germany! Nice and clean explanation
just_a_random on June 04, 2020 at 19:26
clear and concise, just what I like! thanks!
anonymous on June 07, 2020 at 08:39
amazing explanation!!
nimbustrr on June 07, 2020 at 09:39
great details
scrat on June 07, 2020 at 13:46
Excellent article. Nice explanation.
dilip on June 07, 2020 at 22:29
can you explain about int &&a = 5; // a legal statement, why
Triangles on June 11, 2020 at 10:21
@dilip that's an rvalue reference (double ampersand). More information here:
Chris on June 11, 2020 at 18:22
Thanks for helping me on this topic, your explanation is crystal clear and easy to follow !
Trent Di on June 25, 2020 at 03:17
Excellent article!

"The ability to return lvalues from functions looks pretty obscure, yet it is useful when you are doing advanced stuff like implementing some overloaded operators. More on that in future chapters.". Has this been written? :D
Triangles on June 28, 2020 at 11:48
Thank you Trent Di! The chapter hasn't been written yet... hold tight! :)