Skip to content

References

  • rvalue, lvalue
  • Reference

What will happen when this code is run? Why?

Unless main is shown explicitly, assume the code appears in a typical main file:

#include <iostream>
#include <array> // required for std::array
int main()
{
// here
}
5 = 6;
Answer

5 is an rvalue, so it cannot appear on the left-hand side of an assignment.

int* a = &5;
Answer

You cannot take the address of an rvalue. You can take the address only of an lvalue (an expression that designates an object), because a numeric literal does not designate an object in memory.

int* a = &(5 + 6);
Answer

The situation is similar: (5 + 6) is an rvalue. It is impossible to take the address of an rvalue.

int& a = 5;
Answer

An int& can bind only to an lvalue—an expression that designates an object— because a must ultimately refer to an object in memory, not merely a value.

int a = 5;
int& b = a;
std::cout << a << std::endl;
std::cout << b << std::endl;
Answer

Here, b is a reference to a. The compiler may treat b as another name for a or implement it internally using a pointer to a.

When reading a value, using the name b is equivalent to using the name a —both names refer to the same object. Unlike b, a is an object. When its declaration is executed, storage is allocated for it, typically on the stack.

5 will be printed 2 times.

int a = 5;
int& b = a;
b = 6;
std::cout << a << std::endl;
Answer

Here, a will be overwritten with 6 through the name b.

7. Overwriting a variable that has a reference

Section titled “7. Overwriting a variable that has a reference”
int a = 5;
int& b = a;
a = 6;
std::cout << b << std::endl;
Answer

a is overwritten directly.

Since b is a reference to a, reading from b reads from a.

int a = 5;
int& b = a + 7;
std::cout << a << std::endl;
std::cout << b << std::endl;
Answer

a + 7 is an rvalue, not an lvalue. Since a non-const lvalue reference cannot bind to an rvalue, this code will not compile.

9. Assignment operator applied to a reference

Section titled “9. Assignment operator applied to a reference”
int a = 5;
int& b = a;
b += 7;
std::cout << a << std::endl;
std::cout << b << std::endl;
Answer

b += 7 updates a because b refers to a.

The same value will be printed for both variables (12).

int a = 1;
int b = 2;
int& c = a;
c = b;
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << c << std::endl;
Answer

c = b does not rebind the reference; a reference cannot be rebound.

In c = b, you can effectively replace c with the object it refers to (a), giving a = b, which overwrites a with the value from b, 2.

The output will be:

2
2
2

11. Overwriting the referenced variable after assigning through the reference

Section titled “11. Overwriting the referenced variable after assigning through the reference”
int a = 1;
int b = 2;
int& c = a;
c = b;
c = 3;
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << c << std::endl;
Answer

Unlike pointers, references cannot be “redirected”.

This illustrates that c continues to refer to a, even after the line c = b.

The output will be:

3
2
3
int a = 1;
int* pa = &a;
int& b = *pa;
*pa = 2;
b = 3;
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << *pa << std::endl;
Answer

int& b = *pa; creates a reference to the object pointed to by pa. All 3 expressions now refer to the same object, a.

3 will be printed 3 times.

13. Reassigning a pointer after creating a reference from it

Section titled “13. Reassigning a pointer after creating a reference from it”
int a = 1;
int b = 2;
int* p = &a;
int& r = *p;
p = &b;
r = 3;
std::cout << a << std::endl;
std::cout << b << std::endl;
Answer

Reassigning p does not change what r refers to, so r = 3 changes a. The output will be:

3
2

14. Overwriting a variable through an address and a reference

Section titled “14. Overwriting a variable through an address and a reference”
int a = 1;
int& b = a;
int* c = &b;
*c = 2;
std::cout << a << std::endl;
Answer

You can also obtain an address from a reference. Here, *c = 2; will overwrite a.

#include <iostream>
struct Position
{
int x;
int y;
};
int main()
{
Position a{ .x = 1, .y = 2 };
Position& ra = a;
int& rx = ra.x;
ra.x = 3;
ra.y = 4;
int& ry = ra.y;
std::cout << rx << std::endl;
std::cout << ry << std::endl;
std::cout << ra.x << std::endl;
std::cout << ra.y << std::endl;
std::cout << a.x << std::endl;
std::cout << a.y << std::endl;
}
Answer

References can be created to members of larger objects.

Here, 3 and 4 will be printed everywhere.

int a = 1;
int b = 2;
int* pa = &a;
int*& rpa = pa;
pa = &b;
*rpa = 3;
std::cout << a << std::endl;
std::cout << b << std::endl;
Answer

You can create references to pointers because pointers are also objects that store addresses.

Here, we replace the address stored in pa. The reference rpa reflects that change, so dereferencing it accesses b.

*rpa -> *pa -> *(&b) -> b

a will be 1, and b will be 3.

int a = 1;
int b = 2;
int* pa = &a;
int*& rpa = pa;
pa = &b;
int& rb = *rpa;
rb = 3;
std::cout << a << std::endl;
std::cout << b << std::endl;
Answer

This illustrates that references can be bound to any expression that ultimately yields an lvalue.

rb = 3 will write 3 into b.

Is it possible to obtain the size of a reference? What is the size of a reference?

#include <iostream>
int main()
{
int a = 6;
int& b = a;
std::cout << sizeof(b) << std::endl;
}
Answer

sizeof(b) works, but it gives the size of an int, not a reference.

The size of a reference itself cannot be obtained; its representation is an implementation detail.

Many compilers implement a reference parameter by passing an address, but this is also an implementation detail. Such an address is commonly 8 bytes on modern platforms.

A function needs information from outside its local scope to access a particular variable. Passing its address is a straightforward way to provide that information.

#include <iostream>
void resetAmount(int& amount)
{
amount = 0;
}
int main()
{
int appleAmount = 6;
resetAmount(appleAmount);
std::cout << appleAmount << std::endl;
}
Answer

appleAmount will be overwritten with 0 because a reference to it is passed to the function.

Many compilers implement this by passing the address of the appleAmount variable, but that detail is hidden from you.

#include <iostream>
struct Arm
{
int power;
};
void increasePower(Arm& arm)
{
arm.power += 1;
}
int main()
{
Arm arm { 1 };
increasePower(arm);
std::cout << arm.power << std::endl;
}
Answer

You can pass references to struct objects to a function. Here, the function increases the power.

int a = 5;
auto b = a;
b = 6;
std::cout << a << std::endl;
Answer

auto figures out the type (int) but not the reference: b is a separate object holding a copy of a.

b = 6 overwrites only the copy, so 5 will be printed.

int a = 5;
auto& b = a;
b = 6;
std::cout << a << std::endl;
Answer

auto& figures out int and keeps the reference: b is another name for a.

b = 6 overwrites a through that name, so 6 will be printed.

Use auto& when you want to change the original — for example, the elements of an array in a loop — and const auto& when you only want to read it.