Pass-by-const-reference is the default way to hand objects to functions in C++, and references appear in almost every signature.
A reference is another name for an existing object. int& r = a; makes r an alias of a: reading or writing r reads or writes a. A reference must be initialized when it's declared and can never be rebound. r = b; doesn't make r refer to b; it assigns b's value to a.
References are mostly used in function parameters:
void f(T& x): the function works on the caller's object and may modify it ("out" or "in-out" parameter).void f(const T& x): no copy and no modification. This is the standard way to pass anything bigger than a few machine words, such as strings, vectors and user types. A const reference can also bind to temporaries:f(std::string{"tmp"})works.void f(T x): by value, for cheap types likeint,doubleand small structs, or when the function needs its own copy anyway.
In range-for loops, for (auto& x : v) lets you modify elements and for (const auto& x : v) reads them without copying.
The big danger is a dangling reference: a reference that outlives the object it refers to. Returning a reference to a local variable is the classic case; the local is destroyed when the function returns, and using the reference is undefined behaviour. Compilers warn about the obvious cases but not all of them. Returning a reference is fine when the object lives on, such as a member of *this or an element of a container the caller owns.
Unlike pointers, references can't be null and need no special syntax to use, which makes them the default choice for "refer to an object that definitely exists".
Example
#include <iostream>
#include <string>
#include <vector>
void double_all(std::vector<int>& v) { // modifies the caller's vector
for (int& x : v) x *= 2;
}
std::size_t total_length(const std::vector<std::string>& words) { // no copy, read-only
std::size_t n = 0;
for (const auto& w : words) n += w.size();
return n;
}
// int& bad() { int local = 1; return local; } // dangling: local dies on return
int main() {
int a = 1, b = 2;
int& r = a; // r is another name for a
r = b; // assigns 2 to a; does NOT rebind r
r += 10;
std::cout << a << ' ' << b << '\n'; // 12 2
std::vector<int> nums{1, 2, 3};
double_all(nums);
std::cout << nums[2] << '\n'; // 6
std::cout << total_length({"ab", "cde"}) << '\n'; // 5
}Remember
- A reference is an alias: must be initialised, can't be null, can't be rebound
- Pass big objects as
const T&, outputs asT&, small values by value - Never return a reference to a local variable
Watch out for
- Returning a reference (or
std::string_view) to a local or temporary that is destroyed at the end of the function.