Skip to content

Modern C++ Essentials for Interviews

This is a practical reference for the C++ features you actually use when whiteboarding or writing code in an interview. Every section ends with a "when to use this" takeaway so you can reach for the right tool without thinking.

1. Header and Boilerplate

#include <bits/stdc++.h>

This is a GCC-specific header that pulls in every standard library header in one shot. It is non-standard (does not work on MSVC, and technically not portable), but it is universally accepted in competitive programming and interview settings because it saves you from remembering which header contains std::priority_queue vs std::unordered_map.

cpp
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    // your code
    return 0;
}

using namespace std;

In production code this is considered a smell because it drags every standard name into the global namespace and can cause ambiguity (e.g. std::count vs your own count). In an interview it is fine and expected — interviewers care about your algorithm, not your namespace hygiene.

When to use in interviews: always. Start every file with the boilerplate above and focus on the problem.


2. Fast I/O

Standard cin/cout are slow because they are synchronized with C's stdio by default and because cin is flushed and tied to cout so that prompts appear before reads.

cpp
ios_base::sync_with_stdio(false); // untie from C stdio (~10x speedup)
cin.tie(nullptr);                 // stop flushing cout on every cin read

After this, do not mix printf/scanf with cin/cout in the same program.

getline for full lines

cin >> s stops at whitespace. For a whole line of text (including spaces), use getline.

cpp
string line;
getline(cin, line);

// If you previously did cin >> n, there is a leftover '\n' in the buffer:
cin >> n;
cin.ignore();           // discard the newline
getline(cin, line);     // now reads the real next line

Reading until EOF

cpp
int x;
while (cin >> x) {
    // process x
}

// Or line-by-line:
string line;
while (getline(cin, line)) {
    // process line
}

endl vs "\n"

endl writes a newline and flushes the buffer. "\n" just writes a newline. In tight output loops endl can be 100x slower.

cpp
cout << x << "\n"; // preferred
cout << x << endl; // only when you actually need a flush

When to use in interviews: add the fast I/O lines whenever you have heavy input (thousands+ of reads). Always prefer "\n" over endl.


3. Modern C++ Features (C++11/14/17)

auto keyword

auto asks the compiler to deduce the variable's type from its initializer. It is invaluable for iterator and lambda types that would otherwise be unwieldy.

cpp
auto it = mp.find(key);         // instead of unordered_map<string,int>::iterator
auto n  = v.size();             // size_t
auto sq = [](int x) { return x*x; }; // lambda type is anonymous

Do not use auto when the type is short and the initializer is ambiguous. auto x = 0; is clear; auto x = foo(); may hide whether foo returns int or double.

Range-based for loop

cpp
vector<int> v{1,2,3};
for (int x : v)      { /* copy */ }
for (int& x : v)     { /* reference — can modify */ }
for (const auto& x : v) { /* const ref — no copy, read only (best default) */ }

For map/unordered_map:

cpp
for (const auto& [key, value] : mp) {  // C++17 structured binding
    cout << key << " -> " << value << "\n";
}

nullptr vs NULL

NULL is typically a macro for 0, so f(NULL) may call an int overload. nullptr is a real pointer type (std::nullptr_t) and always resolves to a pointer overload. Use nullptr everywhere.

Uniform initialization {} vs ()

cpp
vector<int> a(5, 0);    // vector of 5 zeros
vector<int> b{5, 0};    // vector of two elements: 5 and 0  ← gotcha

int x{3.14};            // error: narrowing conversion
int y(3.14);            // silently truncates to 3

{} prevents narrowing and is preferred for aggregate types, but for containers like vector it forwards to the initializer_list constructor and can surprise you.

constexpr

Computed at compile time when possible. Useful for sizes and mathematical constants.

cpp
constexpr int MOD = 1'000'000'007;
constexpr int N   = 1 << 20;
int fib[N];

Structured bindings (C++17)

Destructure pairs, tuples, structs, and arrays.

cpp
pair<int,int> p{1, 2};
auto [a, b] = p;

map<string,int> mp;
for (auto& [key, value] : mp) { ... }

std::move and rvalue references

std::move casts an lvalue to an rvalue so its resources can be stolen instead of copied. You rarely need to write it yourself in interview code, but recognizing it matters.

cpp
string s = "hello world";
vector<string> v;
v.push_back(std::move(s));  // s is now empty; no copy performed

When to use in interviews: auto for iterators and complex types, auto& in range-for to avoid copies, nullptr always, structured bindings when iterating maps.


4. Lambdas

Basic syntax

cpp
[capture](parameters) -> return_type { body }

The return type can almost always be omitted — the compiler deduces it.

cpp
auto add = [](int a, int b) { return a + b; };
int s = add(2, 3); // 5

Capture modes

CaptureMeaning
[]Capture nothing
[=]Capture all used variables by value
[&]Capture all used variables by reference
[x]Capture x by value
[&x]Capture x by reference
[=, &x]Everything by value except x by reference
[&, x]Everything by reference except x by value
[this]Capture the enclosing object's this pointer
cpp
int offset = 10;
auto shift = [offset](int x) { return x + offset; }; // by value — safe
auto track = [&offset](int x) { offset += x; };      // modifies outer offset

Capturing by reference and letting the lambda outlive the referenced variable is a classic dangling-reference bug.

Lambdas with STL algorithms

cpp
vector<pair<int,string>> v = {{3,"c"},{1,"a"},{2,"b"}};
sort(v.begin(), v.end(), [](const auto& a, const auto& b) {
    return a.first < b.first; // ascending by first
});

int count = count_if(v.begin(), v.end(), [](const auto& p) {
    return p.first > 1;
});

Recursive lambdas with std::function

A lambda has an anonymous type, so it can't refer to itself directly. Wrap it in std::function or pass itself as a parameter.

cpp
function<int(int)> fact = [&](int n) -> int {
    return n <= 1 ? 1 : n * fact(n - 1);
};

// Alternative (C++14, slightly faster — no std::function overhead):
auto dfs = [&](auto&& self, int node) -> void {
    for (int nb : adj[node]) self(self, nb);
};
dfs(dfs, root);

Generic lambdas (C++14)

auto parameters make a lambda a template.

cpp
auto print = [](const auto& x) { cout << x << "\n"; };
print(42);
print("hello");
print(3.14);

When to use in interviews: custom comparators for sort/priority_queue, inline DFS/BFS helpers, predicate arguments for count_if/find_if.


5. Utility Types

pair

cpp
pair<int,string> p{1, "one"};
cout << p.first << " " << p.second;

auto q = make_pair(2, "two"); // auto-deduces types
auto [num, name] = p;         // structured binding

Pairs are ordered lexicographically by default, which is convenient for sorting "by key then value".

tuple

cpp
tuple<int,string,double> t{1, "a", 3.14};
cout << get<0>(t) << " " << get<1>(t) << "\n";

int x; string s; double d;
tie(x, s, d) = t;           // unpack

auto [a, b, c] = t;         // C++17 structured binding

optional (C++17)

Represents "maybe a value", replacing sentinel return values like -1.

cpp
optional<int> find_index(const vector<int>& v, int target) {
    for (int i = 0; i < (int)v.size(); ++i)
        if (v[i] == target) return i;
    return nullopt;
}

auto idx = find_index(v, 5);
if (idx) cout << *idx << "\n"; // or idx.value()

string_view (C++17)

A non-owning view into a string. Passing string_view avoids copies when a function only needs to read.

cpp
int count_vowels(string_view s) {
    int c = 0;
    for (char ch : s) if (string_view("aeiou").find(ch) != string_view::npos) ++c;
    return c;
}
count_vowels("hello");       // no allocation
count_vowels(some_string);   // also no copy

When to use in interviews: pair/tuple for multi-value returns; optional when "not found" is a real case; string_view rarely needed but worth knowing.


6. Numeric Types and Limits

TypeSizeRange (approx)Use when
int32-bit±2.1 × 10^9default integer
long long64-bit±9.2 × 10^18sums/products might overflow
size_t64-bit unsigned (usually)0 to 1.8 × 10^19container sizes, indices
double64-bit15-17 significant digitsfloats; avoid equality compares

Limits

cpp
INT_MAX           // 2,147,483,647
INT_MIN           // -2,147,483,648
LLONG_MAX         // 9,223,372,036,854,775,807
LLONG_MIN

numeric_limits<int>::max();
numeric_limits<double>::infinity();

Integer overflow — the classic bug

cpp
int a = 100000, b = 100000;
long long bad  = a * b;            // WRONG — int*int overflows, then widens
long long good = (long long)a * b; // cast one operand first

Any time you multiply two ints whose product could exceed ~2 × 10^9, cast to long long before the multiply.

When to use in interviews: default to int; switch to long long the moment you see sums of N ≤ 10^5 elements up to 10^9, or any product of ints.


7. String Operations

cpp
string s = "hello";
s.size();                  // 5
s.length();                // same as size
s.empty();                 // false
s.substr(1, 3);            // "ell" — start, length
s.find("ll");              // 2, or string::npos if not found
s + " world";              // "hello world"
s += '!';                  // append a char

Conversions

cpp
string s = to_string(42);        // "42"
int n     = stoi("42");          // 42
long long ll = stoll("10000000000");
double d  = stod("3.14");

// char digit to int:
char c = '7';
int  d2 = c - '0';        // 7

// lowercase / uppercase:
for (char& ch : s) ch = tolower(ch);

Reverse

cpp
string t = s;
reverse(t.begin(), t.end());

Splitting a string

cpp
vector<string> split(const string& s, char delim) {
    vector<string> out;
    stringstream ss(s);
    string tok;
    while (getline(ss, tok, delim)) out.push_back(tok);
    return out;
}

When to use in interviews: keep this file of tricks handy — stoi, to_string, substr, and c - '0' appear in almost every string problem.


8. References vs Pointers vs Values

Pass byCopies?Can modify?Typical use
valueyeslocal copysmall POD (int, char, small structs)
const T&nonodefault for everything else
T&noyesfunction needs to mutate caller's data
T*noyes (if non-const)pointer can be null; ownership contexts
cpp
void process(const vector<int>& v);   // read-only, no copy
void sort_inplace(vector<int>& v);    // mutates caller

for (const auto& x : bigVec)          // no copies in the loop
for (auto& x : bigVec)                // mutate in place
for (auto  x : bigVec)                // copies each element — usually wasteful

When to use in interviews: function params → const T& unless you need to mutate; range-for → const auto& unless you need to mutate.


9. Common Gotchas (CRITICAL)

These bite constantly. Commit them to memory.

Integer overflow on n * n

cpp
int n = 100000;
long long sq = n * n;                 // WRONG — computed as int, overflows
long long sq2 = 1LL * n * n;          // right: promotes to long long
long long sq3 = (long long)n * n;     // also right

Iterator invalidation

cpp
vector<int> v = {1,2,3};
auto it = v.begin();
v.push_back(4);   // may reallocate — `it` is now dangling
*it;              // undefined behavior

Inserting into or erasing from a vector can invalidate every iterator. For map/set, erase only invalidates the erased iterator.

Modifying a container while iterating

cpp
for (auto it = v.begin(); it != v.end(); ++it) {
    if (*it == 0) v.erase(it);   // WRONG — `it` invalidated, loop skips/UB
}

// Correct:
for (auto it = v.begin(); it != v.end(); ) {
    if (*it == 0) it = v.erase(it); // erase returns next valid iterator
    else          ++it;
}

// Or use erase-remove:
v.erase(remove(v.begin(), v.end(), 0), v.end());

auto drops references

cpp
vector<int> v = {1,2,3};
for (auto x : v) x *= 2;    // modifies a copy; v unchanged
for (auto& x : v) x *= 2;   // modifies v

size_t underflow

v.size() returns an unsigned size_t. Subtracting from an empty vector's size wraps to a huge positive number.

cpp
vector<int> v;
for (size_t i = 0; i < v.size() - 1; ++i) { ... } // infinite loop if empty

// Safer:
for (int i = 0; i + 1 < (int)v.size(); ++i) { ... }

map[key] inserts a default

cpp
unordered_map<string,int> mp;
if (mp["missing"] == 0) { ... }     // this INSERTS "missing" with value 0

if (mp.count("missing")) { ... }    // safe: does not insert
if (mp.find("missing") != mp.end()) { ... } // also safe

This can silently change your map's size and ruin iteration bounds.

unordered_map worst-case O(n)

With adversarial inputs (or pathological hashing of int), unordered_map can degenerate to O(n) per op. Competitive judges sometimes exploit this. In interviews it is rarely an issue, but if a solution "should work" and TLEs, consider switching to map (guaranteed O(log n)) or adding a custom hash with a random seed.


10. Useful Built-in Functions

GCC provides several __builtin_* intrinsics that map directly to fast CPU instructions.

cpp
__builtin_popcount(x);       // number of set bits in unsigned int
__builtin_popcountll(x);     // same, for unsigned long long
__builtin_clz(x);            // count leading zeros (x must be != 0)
__builtin_ctz(x);            // count trailing zeros
__builtin_parity(x);         // parity of set bits

__gcd(a, b);                 // greatest common divisor (C++17: use std::gcd)
__lg(x);                     // floor(log2(x)) — integer log

swap(a, b);                  // O(1) for most built-ins and move-aware types

min({1, 5, 3, 2});           // 1 — initializer list overload
max({a, b, c, d});

Standard alternatives that work on any compiler:

cpp
#include <numeric>
gcd(a, b);                   // C++17
lcm(a, b);                   // C++17

When to use in interviews: __builtin_popcount for bit manipulation problems, __gcd for number theory, min({...})/max({...}) to avoid chained calls, swap for in-place algorithms.


Quick Reference Boilerplate

Paste this at the top of every interview solution:

cpp
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    // your solution

    return 0;
}

Frontend interview preparation reference.