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';}5 3 1 4 1 5 apple banana apple cherry banana apple
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&orstd::vector<int>&is another name for the same variable.
The example'sdoubleAll(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 rangev.begin(), v.end().std::sortwith the lambda[](int a, int b) { return a > b; }sorts largest first, andstd::findreturns an iterator to the match (orv.end()), soit - v.begin()is its index. std::accumulate(v.begin(), v.end(), 0)is the sum, and the type of that last0is the type of the sum (0givesint; for big sums use0LL).
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).
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.