Pass by Value vs Reference in C++
Compare pass by value, reference, const reference, and rvalue reference in C++, with performance tradeoffs and move semantics explained.
Expected Interview Answer
Pass by value copies an argument into the function's parameter so changes inside the function never affect the caller's original, while pass by reference binds the parameter directly to the caller's variable so changes inside the function do modify the original.
Pass by value is safe and simple but can be expensive for large objects since a full copy is constructed, unless the compiler elides it or a move is possible. Pass by reference (`T&`) avoids the copy and allows in-place modification, while pass by const reference (`const T&`) avoids the copy but prevents modification, making it the standard choice for large read-only parameters. Pass by pointer is similar to reference but explicitly allows a null value and requires dereferencing syntax at each use. Modern C++ adds pass by rvalue reference (`T&&`) to bind to temporaries and enable move semantics, letting a function steal a temporary's resources instead of copying them; this is why move constructors typically take `T&&` parameters. Choosing correctly is a real performance and correctness decision: small built-in types are usually passed by value, large or non-copyable objects by const reference or by value with std::move when ownership transfers.
- Pass by value guarantees the caller's original stays unmodified
- Pass by reference avoids expensive copies of large objects
- Const reference gives copy-free read access without modification risk
- Rvalue references enable move semantics, avoiding unnecessary deep copies
- Choosing the right one balances performance with safety and intent
AI Mentor Explanation
Pass by value is like handing a teammate a photocopy of the team strategy sheet — they can scribble all over it, but the original coach's copy stays untouched. Pass by reference is like handing them the actual original sheet itself, so any notes they add change the real document everyone else sees later.
Step-by-Step Explanation
Step 1
Pass by value
Declare the parameter as `T param`; the function receives a full copy, so mutations don't affect the caller's original.
Step 2
Pass by reference
Declare the parameter as `T& param` to bind directly to the caller's variable, so mutations inside the function are visible outside it.
Step 3
Pass by const reference
Declare `const T& param` to avoid copying a large object while preventing the function from modifying it.
Step 4
Pass by pointer
Declare `T* param` when the argument may legitimately be absent (`nullptr`), requiring explicit dereferencing at each use.
Step 5
Pass by rvalue reference
Declare `T&& param` to bind to temporaries, enabling move semantics so resources are transferred instead of copied.
Step 6
Choose based on size and intent
Use value for small built-in types, const reference for large read-only objects, and reference/rvalue reference when mutation or ownership transfer is intended.
What Interviewer Expects
- Explains the core difference: copy vs direct binding to the caller's variable
- Knows const reference avoids copies while preventing mutation
- Can distinguish pass by reference from pass by pointer
- Understands rvalue references (`T&&`) and their role in move semantics
- Can justify parameter choice based on type size and mutation intent
Common Mistakes
- Passing large objects by value unnecessarily, causing expensive copies
- Using a non-const reference when the function should not modify the argument
- Confusing a reference parameter with a pointer parameter
- Believing `T&&` always means 'move it', ignoring that named rvalue references are themselves lvalues
- Forgetting that a reference must be bound at initialization and cannot be rebound later
Best Answer (HR Friendly)
“Passing by value gives a function its own private copy of the information so the original data stays safe, while passing by reference gives the function direct access to the original so it can actually change it. Choosing correctly balances performance with keeping your data safe from unintended changes.”
Code Example
#include <iostream>
#include <string>
void byValue(std::string s) {
s += " (modified copy)";
}
void byReference(std::string& s) {
s += " (modified original)";
}
void byConstReference(const std::string& s) {
std::cout << "Read-only: " << s << "\n";
}
int main() {
std::string name = "SkillVeris";
byValue(name);
std::cout << name << "\n"; // SkillVeris (unchanged)
byReference(name);
std::cout << name << "\n"; // SkillVeris (modified original)
byConstReference(name); // Read-only: SkillVeris (modified original)
return 0;
}Follow-up Questions
- When should you prefer `const T&` over passing by value?
- What is the difference between passing by reference and passing by pointer?
- How do rvalue references (`T&&`) enable move semantics in function parameters?
- Is a named rvalue reference parameter itself an lvalue or an rvalue inside the function?
- What happens if you try to bind a temporary to a non-const lvalue reference parameter?
MCQ Practice
1. What happens to the caller's original variable when it is passed by value and modified inside a function?
Pass by value creates a separate copy for the function; modifications inside the function affect only that copy, leaving the caller's original untouched.
2. Why is `const T&` commonly preferred for passing large objects that a function only needs to read?
`const T&` binds directly to the caller's object, avoiding an expensive copy, while the `const` qualifier prevents the function from modifying it.
3. What is the primary purpose of an rvalue reference parameter (`T&&`)?
Rvalue reference parameters bind to temporaries (rvalues), allowing move constructors and move-aware functions to transfer internal resources instead of performing an expensive copy.
Flash Cards
What does pass by value do to the caller's original argument? — Nothing — the function receives an independent copy, so modifications inside don't affect the original.
What does pass by reference allow? — Direct modification of the caller's original variable, since the parameter binds to it rather than copying it.
Why use `const T&` for large objects? — It avoids the copy of pass-by-value while preventing the function from modifying the argument.
What does an rvalue reference parameter (`T&&`) enable? — Move semantics — binding to temporaries and transferring their resources instead of copying them.