Filed Aug 4, 2026
C++ Function Parameters Explained: Pass-by-Value, `const&`, and Mutable References
C++ Function Parameters Explained: Pass-by-Value, const&, and Mutable References
One of the most frequent pieces of advice you’ll hear in C++ is: “Pass by reference to avoid copying.”
While that advice comes from a good place, taking it too literally can lead to subtle performance issues or overly complex code. C++ gives you precise control over how data enters your functions, and picking the right strategy comes down to two simple questions: How big is the data? and Does the function need to modify it?
The Three Main Parameter Styles
In C++, function parameters broadly fall into three categories:
void byValue(int x); // Pass-by-Value (Copy)
void byConstRef(const std::string& s); // Pass-by-Const-Reference (Read-Only)
void byRef(std::vector<int>& vec); // Pass-by-Reference (Mutable)
1. Pass-by-Value (T)
When you pass a variable by value, C++ creates a brand-new copy of that variable inside the function.
int addOne(int num) {
num += 1; // Modifies ONLY the local copy inside addOne
return num;
}
When to use it:
- Primitive Types:
int,float,double,bool,char, and raw pointers. - Small Types: Lightweight structs that fit within CPU registers (usually 16 bytes or smaller).
std::string_view(C++17+): A lightweight view (pointer + size) designed specifically to be passed by value.
Why not use references for small types?
It might seem tempting to write const int& x, but that can actually hurt performance. Small primitive values fit directly inside CPU registers. Passing an int by value drops it straight into a register, whereas passing by reference (const int&) forces the CPU to pass a memory address and perform an extra memory lookup (dereference) to access the value.
2. Pass-by-Const-Reference (const T&)
Passing by const T& passes a direct reference (memory address) to the original object, while the const modifier guarantees the function cannot modify it.
Example: Processing 100,000 Elements
Consider a function that calculates the sum of a vector holding 100,000 integers:
#include <iostream>
#include <vector>
// Fast and safe: Zero copies created
long long calculateSum(const std::vector<int>& numbers) {
long long sum = 0;
for (int num : numbers) {
sum += num;
}
return sum;
}
int main() {
// Create a vector with 100,000 elements
std::vector<int> largeList(100000, 42);
// Passed by const reference: Zero memory allocations
long long total = calculateSum(largeList);
std::cout << "Total sum: " << total << "\n";
}
Why const T& matters here:
If you wrote calculateSum(std::vector<int> numbers) without the reference &:
- C++ would allocate memory on the heap for a second vector.
- It would copy all 100,000 integers one by one before the function even starts running.
- It would deallocate that memory when the function finishes.
By using const std::vector<int>&, the function receives direct read-only access to largeList in zero time with zero memory overhead.
3. Pass-by-Mutable-Reference (T&)
Passing by mutable reference (T&) gives the function direct access to the original variable in memory. Any changes made inside the function happen directly to the caller’s variable.
How it looks in memory:
Pass-by-Value (int x):
main() [ score: 10 ] ──(copies value)──> fn() [ x: 10 ]
│
x = 0 (score stays 10)
Pass-by-Reference (int& x):
main() [ score: 10 ]
▲
│ (x points directly to score's memory space)
fn() [ x ] ────> x = 0 updates main()'s 'score' directly!
Key Use Case: Out-Parameters (Multiple Return Values)
In C++, a return statement can only send back a single value. When you need a function to produce multiple outputs, you can pass caller-owned variables by reference (T&) so the function can fill them in directly—these are known as out-parameters.
#include <iostream>
void getMinMax(int a, int b, int& minOut, int& maxOut) {
if (a < b) {
minOut = a;
maxOut = b;
} else {
minOut = b;
maxOut = a;
}
}
int main() {
int minimum = 0;
int maximum = 0;
// getMinMax writes its results directly into 'minimum' and 'maximum'
getMinMax(42, 17, minimum, maximum);
std::cout << "Min: " << minimum << ", Max: " << maximum << "\n";
// Output: Min: 17, Max: 42
}