C++ lvalue and rvalue

Poby’s Home
4 min readFeb 11, 2021

--

More than the left side or right side…

Value Categories and References

They are totally different. Don’t get confused with these two different things.

Value Categories

  • lvalue / rvalue

References

  • lvalue reference
  • rvalue reference
  • const reference

1. Value Categories

Value categories properties of expressions

  • lvalue
  • rvalue

Identity

  • does the expression have a name?
  • does the expression have a memory location?
  • can you take the address of the expression?

Definition

lvalue

  • has a name
  • must be able to take its address of memory location via &
Foo *pfoo = new Foo;

pfoo is a lvalue

  • the data type is “pointer to Foo”
  • It has a name
  • It can take the address of the pfoo

pfoo is a lvalue

  • even if it doesn't have a name, we can still take the address of *pfoo
int i = 1; // i is lvalue
i = 0; // lvalue can be assigned with a value
int* p = &i; // lvalue can take its address of memory location via &

// function returning a reference is lvalue
int& foo();
foo() = 9; // foo() is an lvalue hence it can get assigned
int* p = &foo(); // foo() is lvalue hence it can take it's address

rvalue

  • does NOT have a name
  • can’t take its address
  • literals (such as 2, “hello world”, true, or nullptr)
  • can’t be assigned with a value
  • temporary objects returned from functions.
  • lifetime usually ends with the current statement
int j = 0;
j = 4; // j is lvalue
// a function returning a value is rvalue
int boo();
j = boo(); // np, j is lvalue, boo() is an rvalue
int* p = &boo(); // error, cannot take the address of an rvalue

2. Pass by value vs Pass by reference

Pass by Value

class Foo{};
void func(Foo f); // f is passed by value
Foo foo; // foo is lvalue
func(foo); // call with an lvalue - valid
func(Foo{}); // call with an rvalue - valid

Pass by Reference

class Foo{};
void func(Foo& f); // f is passed by lvalue reference
Foo foo; // foo is lvalue
func(foo); // call with an lvalue - valid
func(Foo{}); // call with an rvalue - error

to fix the above,

void func(const Foo& f); // f is passed by const reference

or

class Foo{};
void func(Foo&& f); // f is passed by rvalue reference
Foo foo; // foo is lvalue
// func(foo); // call with an lvalue - error
func(std::move(foo)) // need to cast lvalue foo to rvalue using move
func(Foo{}); // call with an rvalue - valid

3. References

lvalue reference

  • called method or function can/will modify the data
  • the caller will observe any modifications made

const reference

  • called method or function can NOT modify the data

rvalue reference

  • called method or function can/will modify the data
  • the caller will not and should not observe any modification made
  • declared using &&

C++ 11 extended the notion of rvalues by letting you bind an rvalue (category) to an rvalue reference.

Examples

Examples from codespice. (see the end of the article for link)

4. Code Practice

below code will end up with an error.

This is invalid. Since the callee can change but the caller can’t see the change made by the callee. So, this is wrong.

error message

It says, “cannot bind non-const lvalue reference of ….”

Solutions

Solution 1

Add const

Solution 2

Provide a function that takes rvalue reference for a parameter.

Caveat:

The second function with rvalue reference as its parameter will take the only rvalue as an argument, such as literal or temporary value (such as “hello world”)

Code Practice 2

I guess nobody will code like below, but if you do, it will be disastrous and will end up crashing because you are returning a reference to a temporary variable in the stack that will go away.

Fix

Still, I discourage doing this but below can fix it.

Disclaimer

I studied lvalue and rvalue concept with copperspice c++ education materials and this page includes some summary notes from it.

--

--

No responses yet