{ }codelper

Learn › C++

Language-specific · 42

C++ references and the STL

& and const references, vector/map/set/unordered_map, algorithms that take lambdas, and the copy, invalidation and auto-insert traps.

This lesson is for C++.
C has neither references nor containers, so it uses pointers and arrays you manage yourself.
Python, Java, C# and JavaScript have their own collections (see the list and dictionary lessons), and a list you pass is always shared, so they need no &.

In C++, passing a value copies it, while a reference & works on the original.
The example below doubles a vector through a reference, uses STL algorithms such as sort, find and sum, and then counts words with a std::map.

#include <algorithm>#include <iostream>#include <map>#include <numeric>#include <set>#include <string>#include <vector> void doubleAll(std::vector<int>& v) {          // & : 원본을 바꿔요    for (auto& x : v) x *= 2;} int total(const std::vector<int>& v) {         // const& : 복사 없이 읽기만    return std::accumulate(v.begin(), v.end(), 0);} void print(const std::vector<int>& v) {    for (size_t i = 0; i < v.size(); i++) std::cout << (i ? " " : "") << v[i];    std::cout << '\n';} int main() {    int n;    std::cin >> n;    std::vector<int> v(n);    for (auto& x : v) std::cin >> x;     for (auto x : v) x = 0;                     // 복사본만 바뀌어요    print(v);    doubleAll(v);    print(v);    std::cout << total(v) << '\n';     std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });    print(v);    auto it = std::find(v.begin(), v.end(), 8);    std::cout << (it - v.begin()) << ' ' << std::set<int>(v.begin(), v.end()).size() << '\n';     std::map<std::string, int> count;    std::string w;    while (std::cin >> w) count[w]++;    for (const auto& [word, c] : count) std::cout << word << ' ' << c << '\n';    std::cout << count.contains("kiwi") << ' ' << count.size() << '\n';    int k = count["kiwi"];                      // 없으면 0으로 새로 넣어요    std::cout << k << ' ' << count.size() << '\n';}
stdin
5
3 1 4 1 5
apple banana apple cherry banana apple
stdout
3 1 4 1 5
6 2 8 2 10
28
10 8 6 2 2
1 4
apple 3
banana 2
cherry 1
0 3
0 4

Key points

  • Passing by value copies every element of a vector, so changes inside the function leave the original untouched.
  • A reference int& or std::vector<int>& is another name for the same variable.
    The example's doubleAll(v) doubles the original.
    A reference must be bound when it is made, cannot be rebound later, and, unlike a pointer, cannot be empty (null).
  • If you only read, take const std::vector<int>&.
    Nothing is copied, and changing it by mistake is a compile error.
  • STL containers: std::vector (a growable array), std::map (a dictionary sorted by key, O(log n) lookup), std::set (a sorted collection without duplicates), std::unordered_map (a hash dictionary, O(1) on average, no fixed order).
  • Algorithms (<algorithm>, <numeric>) take an iterator range v.begin(), v.end().
    std::sort with the lambda [](int a, int b) { return a > b; } sorts largest first, and std::find returns an iterator to the match (or v.end()), so it - v.begin() is its index.
  • std::accumulate(v.begin(), v.end(), 0) is the sum, and the type of that last 0 is the type of the sum (0 gives int; for big sums use 0LL).

How each language writes it

C++The usual habit: take small values (int, double) by value and big ones like string, vector and map by const&.
for (const auto& [word, c] : count) unpacks key and value together (C++17).
C++20 also lets you pass a whole range, as in std::ranges::sort(v)

Common mistakes

A range-for that walks copies

for (auto x : v) x = 0;

→ x is a copy of each element, so v does not change at all (the example's first line, 3 1 4 1 5). clang warns "set but not used" with -Wall

Write auto& x to change elements, or const auto& x to read them without copying.

Using an old reference after push_back

int& first = v[0];
v.push_back(9);
std::cout << first;

→ When the size outgrows the capacity, the vector moves to new memory, and every iterator, pointer and reference taken before becomes invalid. Using one is undefined behavior

After push_back, get the element again by index.
Do not push_back into the same vector while a range-for is walking it.

Asking whether a key exists with map[key]

int k = count["kiwi"];

→ It looks like a read, but a missing key is inserted with 0. In the example the count of entries grows from 3 to 4. That is why [] cannot be used on a const std::map& at all

To ask whether it is there, use count.contains("kiwi") (C++20), count.count(...) or count.find(...); to get an error when it is missing, use count.at(...) (throws std::out_of_range).

How languages differ
for (auto x : v) walks over copies.
Use auto& to change elements and const auto& to only read them, and take big function parameters by const& as well.

Practice