C++ references and pass by reference explained in simple terms: a reference is an alias to an existing variable. Instead of making a copy, you give a function or another part of your program direct access to the original value. This tutorial is a step-by-step, beginner-friendly C++ reference tutorial for beginners. We will cover what a reference is in C++, how pass by reference works, when to use it vs pass by value, how to use references in functions, and common mistakes to avoid. By the end, you will know exactly when to use references instead of pointers and how to write clear, efficient function parameters.
- A reference (T&) is an alias; modifying it changes the original variable.
- Use const T& to read large/complex objects without copying.
- Prefer pass by value for small, cheap-to-copy types (int, double, small structs).
- Use non-const T& only for clear, intentional in-out parameters; document side effects.
- References cannot be null or reseated; don’t return references to local variables.
What is a reference in C++?
A reference in C++ is another name for an existing object. Once initialized, a reference must always refer to the same object and cannot be reseated. You access the same memory as the original variable, so changes through the reference affect the original.
Reference vs pointer (beginner view)
- Reference: syntax uses
&in the type (for example,int&), must be initialized, cannot be null, and does not need dereferencing with*. - Pointer: can be reseated and can be null, uses
*to declare and dereference, and needs&/*operations to access the pointee.
Simple C++ reference examples
#class="cd-package">include <iostream>
int main() {
int a = 10;
int& r = a; // r is a reference (alias) to a
std::cout << "a = " << a << "n";
r += 5; // modifies ___CDPHSTR2___ through the reference
std::cout << "a after r+=5: " << a << "n";
std::cout << "r value: " << r << "n";
return 0;
}a = 10
a after r+=5: 15
r value: 15Pass by reference vs pass by value in C++
When you pass by value, a function receives a copy of the argument. When you pass by reference, the function receives an alias to the original variable. The focus keyphrase “C++ references and pass by reference explained” often appears when students ask how to avoid unnecessary copying and how to write in-out parameters in C++.
| Feature | Pass by value | Pass by reference (T&) | Pass by const reference (const T&) |
|---|---|---|---|
| Copies data? | Yes | No | No |
| Can modify caller’s variable? | No | Yes | No (read-only) |
| Typical use | Small, cheap-to-copy types (int, double) | Clear in-out parameters that must change | Large objects you don’t want to copy, read-only access |
| Beginner rule of thumb | Use for small numbers, small structs | Use sparingly for in/out behavior | Use for big strings, vectors, or when avoiding copy |
Example: pass by value vs pass by reference
#class="cd-package">include <iostream>
void incByValue(int x) { // gets a copy
++x;
}
void incByRef(int& x) { // aliases the caller's variable
++x;
}
int main() {
int n = 5;
incByValue(n);
std::cout << "After incByValue: " << n << "n";
incByRef(n);
std::cout << "After incByRef: " << n << "n";
}After incByValue: 5
After incByRef: 6How to use references in functions (with examples)
Understanding reference parameters in C++ with examples is the fastest way to build intuition. Here are three common patterns.
1) In-out parameters with non-const reference
Use a non-const reference (T&) when the function must change the caller’s variable. Keep it obvious and intentional—don’t hide side effects.
#class="cd-package">include <iostream>
void swapInts(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 3, y = 7;
swapInts(x, y);
std::cout << "x = " << x << ", y = " << y << "n";
}x = 7, y = 32) Read-only views with const reference
Use const T& to accept large or complex objects without copying them, while guaranteeing you won’t modify them. This is ideal for strings, vectors, or other big types.
#class="cd-package">include <iostream>
#class="cd-package">include <vector>
int sum(const std::vector<int>& v) {
int s = 0;
for (int x : v) s += x;
return s;
}
int main() {
std::vector<int> data{1, 2, 3, 4};
std::cout << "sum = " << sum(data) << "n";
}sum = 103) Prefer pass by value for small trivially copied types
For small types (for example, int, double, small structs), pass by value is simple and often just as fast or faster because it avoids potential aliasing and allows better optimizations. That said, performance depends on context—measure when it matters.
Step-by-step guide to pass by reference in C++
- Decide if the function must modify the caller’s variable.
- If yes, use
T&(non-const reference) and document the in-out behavior. - If not, skip to step 2.
- If yes, use
- Decide if copying is cheap or expensive.
- If cheap (like
int), pass by value. - If expensive (like
std::string,std::vector), pass byconst T&.
- If cheap (like
- Implement the function.
- Use clear parameter names and keep side effects minimal.
- Test with simple input to verify pass-by-reference behavior.
- Check for lifetime issues.
- Don’t store references in longer-lived objects unless you understand ownership and lifetimes.
- Do not return references to local variables.
- Measure if you changed performance-critical code. Don’t assume pass by reference is always faster.
Notes: document in-out side effects; cannot be null or reseated.
If yes → pass by value (T).
If no → pass by const T& (read-only, no copy).
→ Use T* when null/reseating is required.
→ Use smart pointers (
std::unique_ptr, std::shared_ptr) for ownership.
When to use references instead of pointers (for beginners)
- Use references in function parameters when the argument must not be null and you want simple, direct access to the same object.
- Use
const T&for read-only, non-null views of large objects. - Use pointers when null is a valid state, when you need reseating (point to different objects later), or when interfacing with low-level APIs and dynamic arrays.
- For ownership, prefer smart pointers (
std::unique_ptr,std::shared_ptr) instead of raw pointers. References never express ownership.
Beyond basics: lvalue references, rvalue references, and forwarding (optional)
There are two main kinds of references:
- Lvalue references (
T&) bind to named objects (lvalues). Use them for in-out parameters andconst T&for read-only views. - Rvalue references (
T&&) usually bind to temporaries (rvalues) and enable moves. This is useful for performance in some APIs. In templates where the type is deduced,T&&can be a forwarding reference. Usestd::forwardto preserve the argument’s original value category.
Example showing a simple forwarding reference (advanced, but complete):
#class="cd-package">include <iostream>
#class="cd-package">include <utility> // std::forward
template <class T, class Func>
void call_with(T&& arg, Func f) {
f(std::forward<T>(arg));
}
int main() {
auto print = [](const std::string& s){ std::cout << s << "n"; };
std::string name = "CodDesire";
call_with(name, print); // passes as lvalue
call_with(std::string("Hello"), print); // passes as rvalue
}For beginners, you can safely focus on T& and const T&. Use rvalue references and forwarding later when you learn move semantics.
Common mistakes with C++ references and how to fix them
- “My reference did not update the original variable.”
- Cause: The function parameter was by value (
T) instead of by reference (T&). - Fix: Change the parameter to
T&for in-out orconst T&for read-only without copying.
- Cause: The function parameter was by value (
- Returning a reference to a local variable.
- Cause: The local variable is destroyed when the function returns, leaving a dangling reference.
- Fix: Return by value, or return a reference only to an object that outlives the function (for example, a static or a member with a known lifetime). Prefer returning values for simplicity.
- Binding a non-const reference to a temporary.
- Cause: C++ does not allow
T&to bind to a temporary. Onlyconst T&or suitable rvalue references can bind to temporaries. - Fix: Use
const T&if you just need to read, or structure your code to avoid binding non-const references to temporaries.
- Cause: C++ does not allow
- Storing references in standard containers.
- Cause: Containers store values, not references. You can’t have
std::vector<int&>. - Fix: Store pointers or use
std::reference_wrapper<T>viastd::ref/std::crefif you truly need reference-like behavior.
- Cause: Containers store values, not references. You can’t have
- Assuming pass-by-reference is always faster.
- Cause: Not all copies are expensive. Small types are cheap to copy, and pass-by-value can help optimization.
- Fix: Follow common guidelines: small types by value, big/expensive-to-copy by
const&, and non-const&only when mutation is required.
- Misusing
std::move.- Cause: Thinking
std::movemoves data by itself. - Fix:
std::moveonly casts to an rvalue. Actual moving depends on the target type’s move operations.
- Cause: Thinking
FAQ: C++ pass by reference explained in simple terms
What is a reference in C++ and how is it different from a pointer?
A reference is an alias to an existing object and must be initialized to refer to something. It cannot be null or reseated. A pointer holds an address, can be null, can point to different objects over time, and requires explicit dereferencing.
How does pass by reference work in C++?
The function parameter is declared as T& (or const T&). The function receives direct access to the caller’s object. Modifying a T& parameter changes the caller’s variable; const T& prevents modification while avoiding copies.
When should I use pass by reference instead of pass by value in C++?
- Use pass by value for small, cheap-to-copy types.
- Use
const T&for large or expensive-to-copy objects when you only need to read. - Use
T&when the function must modify the caller’s variable (clear in-out semantics).
Can I return a reference from a C++ function and is it safe?
It’s safe only if the reference refers to an object that outlives the function (for example, a static variable, a global, or a member in an object that stays alive). Never return a reference to a local variable.
Why is my C++ reference not updating the original variable?
Most likely the parameter was passed by value, not by reference. Ensure the function signature uses T&. Also confirm you’re calling the function with a variable (an lvalue) and not with a temporary that binds to a const T& parameter.
Practical guidelines you can trust
- For cheap types (int, double, small structs): pass by value.
- For large or polymorphic types: pass by
const&. - For in-out modification: pass by non-const
&and document behavior. - Return by value unless you have a strong reason to return a reference.
- Adopt move and forwarding semantics later; they are powerful but advanced.
- Will the function modify the caller’s object? If yes, use
T&; otherwise prefer value orconst T&. - Is the type cheap to copy? If yes, pass by value; if no, use
const T&. - Do you need null/reseating? If yes, use a pointer or a smart pointer, not a reference.
- Never return a reference to a local; prefer return by value unless lifetime is guaranteed.
- Don’t store raw references in containers; use pointers or
std::reference_wrapper. - Avoid binding non-const
T&to temporaries; useconst T&or restructure. - Document in-out behavior for non-const references and test with simple examples.
Keep learning
Continue your C++ journey in our C++ category with more beginner-friendly lessons and simple examples: CodDesire C++ Tutorials.
Sources / Further reading
- cppreference: References (lvalue/rvalue, reference collapsing) — https://en.cppreference.com/cpp/language/reference
- cppreference: Reference initialization and temporaries — https://en.cppreference.com/cpp/language/reference_initialization
- cppreference: std::forward — https://en.cppreference.com/cpp/utility/forward
- Microsoft Learn: rvalue references overview — https://learn.microsoft.com/en-us/cpp/cpp/rvalue-reference-declarator-amp-amp?view=msvc-170
- C++ Core Guidelines (parameter passing) — https://isocpp.org/guidelines
- cppreference: Member functions and ref-qualifiers — https://en.cppreference.com/cpp/language/member_functions
This page is a simple, step by step guide to pass by reference in C++. With the examples and explanations above, you now have C++ references and pass by reference explained in clear, beginner-friendly language. Practice by writing small functions and verifying their behavior with prints and tests.


