C++ Cheatsheet

STL Containers

Use this C++ reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Container Overview

ContainerHeaderOrdered?AccessInsert/EraseUse when
vector<T><vector>sequenceO(1) randomO(1) back, O(n) middynamic array, most-used default
deque<T><deque>sequenceO(1) randomO(1) front+back, O(n) midqueue with random access
list<T><list>sequenceO(n)O(1) anywhere (with iterator)frequent mid-insert/erase
forward_list<T><forward_list>sequenceO(n)O(1) after positionsingly-linked; minimal memory
array<T,N><array>sequenceO(1) randomfixed sizeC-array replacement
stack<T><stack>adaptertop onlyO(1) push/popLIFO
queue<T><queue>adapterfront/backO(1) push/popFIFO
priority_queue<T><queue>adaptermax topO(log n) push/popheap / scheduling
set<K><set>sortedO(log n)O(log n)sorted unique keys
multiset<K><set>sortedO(log n)O(log n)sorted keys with duplicates
map<K,V><map>sortedO(log n)O(log n)sorted key-value
multimap<K,V><map>sortedO(log n)O(log n)sorted kv, duplicate keys
unordered_set<K><unordered_set>hashO(1) avgO(1) avgfast unique membership
unordered_multiset<K><unordered_set>hashO(1) avgO(1) avgfast membership, duplicates
unordered_map<K,V><unordered_map>hashO(1) avgO(1) avgfast key-value lookup
unordered_multimap<K,V><unordered_map>hashO(1) avgO(1) avgfast kv, duplicate keys
span<T><span>viewO(1)non-owningnon-owning range view (C++20)
string_view<string_view>viewO(1)non-owningcheap string reference

vector<T>

#include <vector>

std::vector<int> v;
std::vector<int> v2(5);           // 5 elements, value-initialized (0)
std::vector<int> v3(5, 42);       // 5 elements, each 42
std::vector<int> v4 = {1, 2, 3};  // initializer list
std::vector<int> v5(v4);          // copy
std::vector<int> v6(std::move(v4)); // move (v4 emptied)

// Capacity / size
v.size();           // number of elements
v.empty();          // true if size == 0
v.capacity();       // allocated capacity
v.reserve(100);     // allocate at least 100 (no new elements)
v.resize(10);       // set size to 10 (new elements value-init)
v.resize(10, 99);   // new elements = 99
v.shrink_to_fit();  // release excess capacity

// Element access
v[i];               // no bounds check (UB if out of range)
v.at(i);            // bounds-checked; throws std::out_of_range
v.front();          // first element
v.back();           // last element
v.data();           // raw pointer to first element

// Modifiers
v.push_back(42);            // append copy
v.push_back(std::move(x));  // append move
v.emplace_back(42);         // construct in-place (no copy)
v.pop_back();               // remove last (no return)
v.insert(v.begin() + 2, 99);           // insert at position
v.insert(v.end(), {7, 8, 9});          // insert range
v.emplace(v.begin() + 1, 42);         // emplace at position
v.erase(v.begin() + 2);               // erase one element
v.erase(v.begin(), v.begin() + 3);    // erase range
v.clear();                             // remove all elements
v.assign(5, 0);                        // replace contents

// Iteration
for (auto& x : v) { /* ... */ }
for (auto it = v.begin(); it != v.end(); ++it) { *it; }
v.rbegin(); v.rend();   // reverse iterators
v.cbegin(); v.cend();   // const iterators

array<T, N>

#include <array>

std::array<int, 4> a = {1, 2, 3, 4};
std::array<int, 4> b{};               // zero-initialized

a[i];  a.at(i);  a.front();  a.back();
a.size();                              // always N (constexpr)
a.empty();                             // true only if N == 0
a.data();                              // raw pointer
a.fill(0);                             // set all to 0
std::swap(a, b);                       // O(N) element-wise swap

deque<T>

#include <deque>

std::deque<int> d = {1, 2, 3};
d.push_back(4);
d.push_front(0);
d.pop_back();
d.pop_front();
d[i]; d.at(i); d.front(); d.back();
d.insert(d.begin() + 1, 99);
d.erase(d.begin());

list<T> and forward_list<T>

#include <list>

std::list<int> lst = {1, 2, 3};
lst.push_back(4);
lst.push_front(0);
lst.pop_back();
lst.pop_front();
lst.front(); lst.back();

// Only list (not forward_list) has back() and reverse iterators
auto it = std::find(lst.begin(), lst.end(), 2);
lst.insert(it, 99);        // insert BEFORE it
lst.erase(it);

lst.sort();                // O(n log n) sort (std::sort needs random-access)
lst.sort(std::greater<>{});
lst.reverse();
lst.unique();              // remove consecutive duplicates
lst.remove(2);             // remove all elements == 2
lst.remove_if([](int x){ return x < 0; });
lst.merge(other);          // merge two sorted lists (other emptied)
lst.splice(it, other);     // move elements from other

#include <forward_list>
std::forward_list<int> fl = {1, 2, 3};
fl.push_front(0);
fl.pop_front();
fl.insert_after(fl.begin(), 99);
fl.erase_after(fl.begin());
fl.before_begin();         // iterator before first element

stack<T> and queue<T>

#include <stack>
std::stack<int> st;
st.push(1); st.push(2);
st.top();           // peek (no pop)
st.pop();           // remove top (returns void)
st.empty(); st.size();

#include <queue>
std::queue<int> q;
q.push(1); q.push(2);
q.front();          // peek front
q.back();           // peek back
q.pop();            // remove front (returns void)
q.empty(); q.size();

priority_queue<T>

#include <queue>

// Max-heap by default
std::priority_queue<int> pq;
pq.push(3); pq.push(1); pq.push(4);
pq.top();   // 4 (max)
pq.pop();
pq.empty(); pq.size();

// Min-heap
std::priority_queue<int, std::vector<int>, std::greater<int>> minPQ;

// Custom comparator
auto cmp = [](const std::pair<int,int>& a, const std::pair<int,int>& b){
    return a.second > b.second;  // min by second
};
std::priority_queue<std::pair<int,int>,
                    std::vector<std::pair<int,int>>,
                    decltype(cmp)> custom(cmp);

set<K> and multiset<K>

#include <set>

std::set<int> s = {3, 1, 4, 1, 5};  // duplicates ignored: {1,3,4,5}
s.insert(2);                          // returns pair<iterator, bool>
s.insert({6, 7});
s.erase(3);                           // erase by value
s.erase(s.begin());                   // erase by iterator
s.count(4);                           // 0 or 1 for set
s.find(4);                            // iterator or s.end()
s.contains(4);                        // bool (C++20)
s.lower_bound(3);                     // first >= 3
s.upper_bound(3);                     // first >  3
s.equal_range(3);                     // pair of iterators

// Custom comparator
std::set<int, std::greater<int>> desc;  // descending

std::multiset<int> ms;
ms.insert(3); ms.insert(3);  // duplicates allowed
ms.count(3);                  // can be > 1
ms.erase(ms.find(3));         // erase just ONE occurrence

map<K,V> and multimap<K,V>

#include <map>

std::map<std::string, int> m;
m["alice"] = 30;                 // insert or update via subscript
m.insert({"bob", 25});           // returns pair<iterator, bool>
m.insert_or_assign("alice", 31); // always overwrites (C++17)
m.emplace("carol", 28);
m.try_emplace("dave", 22);       // only inserts if key absent (C++17)
m.at("alice");                   // throws if absent
m["missing"];                    // inserts default value (!)
m.count("alice");                // 0 or 1
m.find("alice");                 // iterator or m.end()
m.contains("alice");             // bool (C++20)
m.erase("alice");
m.lower_bound("b"); m.upper_bound("b"); m.equal_range("b");

for (auto& [key, val] : m) { /* sorted by key */ }

// Merge (C++17) — move nodes from source without reallocation
m.merge(other);

std::multimap<std::string, int> mm;
mm.insert({"k", 1}); mm.insert({"k", 2});  // duplicate keys OK
mm.count("k");  // 2
auto range = mm.equal_range("k");
for (auto it = range.first; it != range.second; ++it) { it->second; }

unordered_map<K,V> and unordered_set<K>

#include <unordered_map>
#include <unordered_set>

std::unordered_map<std::string, int> um;
um["a"] = 1;
um.insert({"b", 2});
um.insert_or_assign("a", 99);
um.try_emplace("c", 3);
um.at("a");  um["missing"];  um.count("a");  um.find("a");  um.contains("a");
um.erase("a");
um.size();  um.empty();  um.clear();

// Bucket interface (for tuning)
um.bucket_count();
um.load_factor();
um.max_load_factor(0.7f);
um.rehash(100);             // at least 100 buckets
um.reserve(50);             // rehash for at least 50 elements

// Custom hash
struct MyHash {
    std::size_t operator()(const MyKey& k) const noexcept {
        return std::hash<int>{}(k.id) ^ (std::hash<std::string>{}(k.name) << 1);
    }
};
std::unordered_map<MyKey, int, MyHash> custom;

std::unordered_set<int> us = {1, 2, 3};
us.insert(4);
us.erase(2);
us.count(3);   // 0 or 1
us.contains(3);

Common Operations Across Containers

// Iteration — works for all standard containers
for (auto& x : container) { /* x is the element (value_type) */ }

// Size
container.size();    // current number of elements
container.empty();   // true if size() == 0
container.max_size();// theoretical maximum

// Clear
container.clear();

// Swap
a.swap(b);           // O(1) pointer swap — except std::array: O(N) element-wise
std::swap(a, b);     // same

// Erase-remove idiom (pre-C++20)
v.erase(std::remove(v.begin(), v.end(), val), v.end());

// C++20 std::erase / std::erase_if
#include <vector>   // (also available for other containers)
std::erase(v, val);                              // remove all == val
std::erase_if(v, [](int x){ return x < 0; });   // remove if pred

std::pair and std::tuple

#include <utility>
std::pair<int, std::string> p = {1, "a"};
p.first;  p.second;
auto [a, b] = p;  // structured binding (C++17)
std::make_pair(1, "a");

#include <tuple>
std::tuple<int, double, std::string> t = {1, 3.14, "hi"};
std::get<0>(t);  std::get<1>(t);  std::get<2>(t);
std::get<double>(t);  // by type (C++14, unique types only)
auto [x, y, z] = t;
std::make_tuple(1, 3.14, "hi");
std::tie(x, y, z) = t;           // assign to existing variables
std::tuple_size_v<decltype(t)>;  // 3