References
A reference is a second name for an existing variable, written with & . References let functions modify the caller's data, avoid expensive copies with const& , and underpin the swap idiom. This lesson makes references click — and shows the dangling-reference trap to avoid.
Learn References in our free C++ course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick recall.
Part of the free C++ course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn
💡 Real-World Analogy
A reference is a nickname . If everyone in the office calls "Elizabeth" by "Liz", then "Liz" and "Elizabeth" are the same person — message either name and the same person hears it. You can't later decide that "Liz" now means a different colleague (a reference can't be rebound), and a nickname only makes sense once the person exists (a reference must be initialized). Passing by reference is like giving a colleague your real phone, not a photocopy of it — anything they change, you see when it comes back.
1. A Reference Is an Alias
Declare a reference with type& name = existing; . From then on the reference and the original are the same object — change one and the other changes too. References must be initialized when created and can never be made to refer to a different object afterward.
2. Pass by Value vs Pass by Reference
By default C++ copies arguments, so a function works on its own copy and the caller's variable is untouched. Take a parameter by reference ( int& n ) and the function operates on the caller's actual variable — changes persist after the call returns.
3. The Swap Idiom
Swapping two variables is the textbook reason references exist. Because both parameters are references, the function exchanges the caller's actual values — something impossible with pass-by-value. (The standard library's std::swap does exactly this for you.)
4. const References: Read Without Copying
For large objects like strings and vectors, copying them into a function is wasteful. A const reference ( const string& s ) gives the function direct, read-only access — no copy, and the compiler guarantees the function can't modify your data. This is the standard way to pass big objects you only need to read.
5. References in Range-Based Loops
In a range-based for , the loop variable is a copy by default. Use auto& to modify each element in place, and const auto& to read each element efficiently without copying. Choosing the right one is a small habit that pays off in correctness and speed.
Your turn. Make this function actually change the caller's variable:
🧩 Reorder Challenge
These lines should increment a variable through a reference parameter and print the new value. Reorder so the program prints 6 .
Why: bump must be defined before main calls it. Inside main you declare score = 5 , then pass it to bump . Because the parameter is int& , n += 1 modifies the caller's score directly, so printing afterward shows 6 .
🧠 Quick Recall
Predict the output before revealing each answer.
7 — r is an alias for a , so assigning through r changes a .
void f(int n){ n = 99; } int x = 1; f(x); cout << x;
1 — n is a by-value copy, so the change inside f doesn't affect x .
vector<int> v = {1,2}; for (auto& e : v) e += 5; cout << v[0] << v[1];
67 — auto& modifies each element in place, so the vector becomes {6, 7} .
Pro Tips
- 💡 Default to const& for big read-only params: strings, vectors, and custom classes.
- 💡 Use plain reference to "return" extra results via out-parameters, like a swap or min/max pair.
- 💡 Prefer const auto& in loops when you only read elements — no copies, no accidental edits.
- 💡 Never return a reference to a local: it dangles the moment the function returns.
Common Errors (and the fix)
- Uninitialized reference: int& r; won't compile — a reference must bind to something immediately.
- Expecting by-value to mutate: if a function doesn't change your variable, you forgot the & on the parameter.
- Dangling reference: returning a reference to a local variable is undefined behavior. Return by value or reference a longer-lived object.
- Copying in a loop: for (auto e : bigVec) copies every element. Use const auto& to read efficiently.
📋 Quick Reference
Syntax
Meaning
Note
int& r = x;
reference (alias) to x
r and x are the same object
int& r;
uninitialized reference
error — must bind at creation
void f(int n)
pass by value
works on a copy; caller unchanged
void f(int& n)
pass by reference
edits the caller's variable
void f(const string& s)
pass by const reference
read big objects with no copy
void swap(int& a, int& b)
out-parameters
return extra results via references
for (auto x : v)
loop over copies
edits to x don't affect v
for (auto& x : v)
loop by reference
modify each element in place
for (const auto& x : v)
read-only loop
best default — no copy, no edits
return localRef;
dangling reference
undefined behavior — never return a local ref
std::swap(a, b);
library swap
uses references under the hood
Frequently Asked Questions
Mini-Challenge: Min and Max
Write a function that returns two results through reference out-parameters. Match the expected output in the comments.
🎉 Lesson Complete
- ✅ A reference ( int& r = x ) is an alias — changes flow both ways
- ✅ Pass by reference to let a function modify the caller's variable
- ✅ Swap and other multi-result functions rely on references
- ✅ const& reads big objects without copying
- ✅ auto& modifies loop elements; const auto& reads them cheaply
- ✅ Next: a checkpoint that combines everything you've learned so far
Practice quiz
How do you declare a reference named r that aliases an existing int x?
- int& r = x;
- int* r = x;
- int r = &x;
- ref int r = x;
Answer: int& r = x;. A reference is written type& name = existing;, so int& r = x; makes r another name for x.
What does this print? int a = 3; int& r = a; r = 7; cout << a;
- 3
- 7
- 0
- It does not compile
Answer: 7. r is an alias for a, so assigning 7 through r changes a itself. It prints 7.
What does this print? void f(int n){ n = 99; } int x = 1; f(x); cout << x;
- 99
- 0
- 1
- It does not compile
Answer: 1. n is taken by value, so f works on a copy. The caller's x is unchanged and 1 prints.
Which parameter type lets a function modify the caller's variable?
- int n
- const int& n
- int& n
- const int n
Answer: int& n. A plain (non-const) reference int& n aliases the caller's variable, so changes persist after the call.
Why pass a large object as const std::string& s?
- It copies the string so the original is safe
- It reads the object without copying and forbids modifying it
- It allows the function to resize the string
- It is required by the compiler for all strings
Answer: It reads the object without copying and forbids modifying it. A const reference gives direct read-only access with no copy, and the const promises the function won't modify it.
Why must both swap parameters be references in void swap2(int& a, int& b)?
- References are faster to type
- So the function exchanges the caller's actual values, not copies
- Because swap can only take two arguments
- To avoid including <utility>
Answer: So the function exchanges the caller's actual values, not copies. Only by taking references does the function operate on the caller's real variables, making the exchange visible afterward.
Continue this course
- Previous: Namespaces
- Next: Checkpoint: C++ Basics