Ch6.3: Passing Arguments
Overview
When you call a function, you pass arguments to its parameters. C++ provides several ways to pass arguments, each with different behavior and performance characteristics.
In this chapter, you will learn:
- pass‑by‑value
- pass‑by‑reference (
&) - pass‑by‑const‑reference (
T const &) - why some functions cannot work without references
- how
::std::ranges::swapand::std::ranges::iter_swapwork
1. Pass‑by‑value
When you pass an argument by value, the function receives its own copy of the argument.
void increment(int x)
{
++x; // modifies the copy
}
int main()
{
int a{5};
increment(a);
// a is still 5
}
Pass‑by‑value is simple and safe, but copying large objects can be expensive.
2. Pass‑by‑reference (&)
When you pass an argument by reference, the function receives an alias to the original object. No copy is made.
void increment(int & x)
{
++x; // modifies the original
}
int main()
{
int a{5};
increment(a);
// a is now 6
}
Pass‑by‑reference allows the function to modify the caller’s variable.
3. Pass‑by‑const‑reference (T const &)
When you pass an argument by const reference, the function receives a reference to the original object, but cannot modify it.
void print_twice(::fast_io::string const & s)
{
::fast_io::println(s);
::fast_io::println(s);
}
This is ideal for large objects such as:
::fast_io::string::fast_io::vector<T>- structs and classes
It avoids copying while guaranteeing that the function cannot change the data.
4. Why pass‑by‑value cannot modify the caller (swap example)
A classic example is a swap function. If you write it using
pass‑by‑value, it will not work.
void bad_swap(int a, int b)
{
int temp{a};
a = b;
b = temp;
}
int main()
{
int x{10};
int y{20};
bad_swap(x, y);
// x is still 10, y is still 20
}
This fails because a and b are copies.
Correct swap using references
void good_swap(int & a, int & b)
{
int temp{a};
a = b;
b = temp;
}
int main()
{
int x{10};
int y{20};
good_swap(x, y);
// x is now 20, y is now 10
}
Here, a and b refer to the original variables.
Swap using ::std::ranges::swap (preferred for objects)
#include <concepts> // defines ::std::ranges::swap
int main()
{
int x{10};
int y{20};
::std::ranges::swap(x, y);
// x is now 20, y is now 10
}
::std::ranges::swap is the modern, standard way to swap objects.
Swap using pointers and ::std::ranges::iter_swap
When you have pointers or iterators, use ::std::ranges::iter_swap.
#include <iterator> // defines ::std::ranges::iter_swap
int main()
{
int x{10};
int y{20};
int * px{::std::addressof(x)};
int * py{::std::addressof(y)};
::std::ranges::iter_swap(px, py); // swaps *px and *py
// x is now 20, y is now 10
}
::std::ranges::iter_swap swaps the values pointed to by the
iterators or pointers.
Where these functions are defined
#include <concepts> // ::std::ranges::swap
#include <iterator> // ::std::ranges::iter_swap
These headers provide the concepts and iterator utilities that power the
ranges versions of swap.
5. When to use each method
Use pass‑by‑value when:
- the parameter is small (e.g.,
int,double) - you need a local copy anyway
Use pass‑by‑reference when:
- the function must modify the caller’s variable
Use pass‑by‑const‑reference when:
- the object is large
- you do not want to copy it
- the function should not modify it
These three forms cover almost all function parameter usage in modern C++.
Key takeaways
- Pass‑by‑value copies the argument.
- Pass‑by‑reference allows modification of the original.
- Pass‑by‑const‑reference avoids copying while preventing modification.
- Use
T const &for large objects such as strings and vectors. - Pass‑by‑value is fine for small types like
intanddouble. - A swap written with pass‑by‑value does not work.
- Use
::std::ranges::swapfor objects. - Use
::std::ranges::iter_swapfor pointers and iterators. ::std::ranges::swapis defined in<concepts>.::std::ranges::iter_swapis defined in<iterator>.