Last 30 Days
No notifications
Modern C++ removes huge classes of bugs and noise. If your code looks like 1998, you're working too hard.
auto — Let the Compiler Type for Youauto i = 42; // int
auto pi = 3.14; // double
auto name = std::string("Asha"); // string (NOT const char*)
auto it = v.begin(); // ::iterator (long type!)for (const auto& [k, v] : map) /* ... */; // structured bindings
> Use auto when the type is obvious from the right side or genuinely cumbersome to spell. Don't hide important types from readers.
forfor (int x : v) cout << x; // copy
for (int& x : v) x *= 2; // mutate
for (const auto& x : v) /* ... */; // const-ref (default for non-trivials)auto add = [](int a, int b) { return a + b; };
add(2, 3);int n = 10;
auto by_n = [n](int x) { return x % n == 0; }; // capture by value
auto count = 0;
for_each(v.begin(), v.end(),
[&count](int x) { if (x > 0) ++count; }); // capture by reference
std::optional — "Maybe a Value"optional<int> parse_int(string_view s) {
try { return stoi(string(s)); }
catch (...) { return nullopt; }
}if (auto x = parse_int("42")) cout << *x;
else cout << "no number";
int v = parse_int("oops").value_or(-1);
Modern C++ (C++11 and later) layered in features that look small but transform daily code. Here's the survival pack.
auto, Type Deduction & decltypeauto x = 42; // int
auto y = 3.14f; // float
auto s = "hello"s; // std::string (with literal suffix)decltype(x) y = x; // y has same type as x
decltype(auto) z = func(); // preserve reference-ness exactly
> Rule: auto is great for *iterators*, *lambdas*, *complex template results*. Resist for short numeric/string types if it hides intent.
for & Structured Bindings (C++17)map<string, int> m{{"a", 1}, {"b", 2}};
for (const auto& [k, v] : m) cout << k << "=" << v << "\n";auto [q, r] = div(17, 5); // q = 3, r = 2
auto [it, inserted] = set.insert(x); // pair returned
[capture-list](params) -> return_type { body }| Capture | Meaning |
[] | None |
[x] | x by value |
[&x] | x by reference |
[=] | All used by value |
[&] | All used by reference |
[this] | Member access |
[x, &y] | mixed |
sort(v.begin(), v.end(),
[](const auto& a, const auto& b) { return a.score > b.score; });auto make_counter = []() {
int n = 0;
return [n]() mutable { return ++n; };
};
auto next = make_counter();
next(); next(); next(); // 1, 2, 3
C++14 added generic lambdas (auto params); C++20 added template lambdas ().
nullptr — Not NULL, Not 0int* p = nullptr; // ✅
if (p) /* never */;
// NULL is just (int)0 — causes overload-resolution bugs.| Pointer | Use |
unique_ptr | Single owner |
shared_ptr | Multi-owner ref-counted |
weak_ptr | Non-owning observer (break cycles) |
Always make_unique / make_shared rather than new.
std::move, Rvalue ReferencesC++11 introduced move semantics so we can transfer ownership of large resources without copying.
vector<int> a(1'000'000);
vector<int> b = std::move(a); // O(1) — steals a's bufferstring greet(string name) {
return "Hello, " + std::move(name);
}
After std::move(a), the moved-from object is in a *valid but unspecified* state — you can assign to it or destroy it, but don't read it.
class Buffer {
vector<int> data;
public:
Buffer(Buffer&& other) noexcept : data(std::move(other.data)) {}
Buffer& operator=(Buffer&&) noexcept = default;
};> Compiler synthesises good defaults — Rule of Zero remains the goal.
std::optional (C++17)A type that either holds a T or holds nothing — replaces sentinel values like -1 / nullptr / empty string.
optional<int> find_age(string_view name);if (auto age = find_age("Asha"))
cout << *age;
else
cout << "unknown";
int safe = find_age("Bob").value_or(0);
std::variant (C++17)Tagged union — type-safe one of these types:
variant<int, string, double> v = 42;
v = "hello"; // now a stringvisit([](const auto& x){ cout << x; }, v);
if (holds_alternative<int>(v)) /* ... */;
std::string_view (C++17)A non-owning view into a string — no allocation, no copy. Use it for parameters when you only need to read:
size_t count_vowels(string_view s) {
size_t n = 0;
for (char c : s) if (string_view{"aeiou"}.contains(c)) ++n;
return n;
}count_vowels("hello"); // works on literals
count_vowels(my_string); // works on string
> Don't store a string_view longer than the string it points to — that's dangling.
std::span (C++20)Like string_view, but for arbitrary contiguous ranges (vector, array, raw arrays). Use it for any function that reads/writes a sequence.
int sum(span<const int> s) {
int total = 0;
for (int x : s) total += x;
return total;
}vector<int> v{1,2,3};
int arr[] = {4,5,6};
sum(v); // ok
sum(arr); // ok
Already met — let you constrain template parameters readably.
#include <concepts>template <std::integral T>
T half(T x) { return x / 2; }
template <typename T>
concept Hashable = requires(T t) { std::hash<T>{}(t); };
#include <ranges>
namespace rv = std::views;
namespace ra = std::ranges;auto pipe = v | rv::filter([](int x){ return x % 2 == 0; })
| rv::transform([](int x){ return x * x; })
| rv::take(3);
ra::sort(v);
ra::find(v, 5);
std::format (C++20)Type-safe modern formatting:
#include <format>
cout << format("Name: {}, Age: {}\n", name, age);
cout << format("{:8.2f}\n", 3.14159);struct Config { int port = 8080; bool debug = false; };
Config c{ .port = 3000, .debug = true };A replacement for header files. Faster compiles, real encapsulation. Compiler/build-tool support is still maturing — adopt cautiously.
// math.cppm
export module math;
export int add(int a, int b) { return a + b; }// main.cpp
import math;
int main() { return add(2, 3); }
if constexpr (C++17) — Compile-Time Branchtemplate <typename T>
auto getValue(T x) {
if constexpr (std::is_pointer_v<T>) return *x;
else return x;
}The dead branch is not even compiled, which means it can use type-specific operations.
constexpr, consteval, constinit| Keyword | Meaning |
constexpr | "Can be evaluated at compile time" |
consteval (C++20) | "MUST be evaluated at compile time" |
constinit (C++20) | "Static initialisation must be a compile-time constant" |
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
constexpr int f5 = factorial(5); // computed at compile time → 120[[attributes]]Pure compiler hints. Common ones:
[[nodiscard]] int compute(); // warn if return value ignored
[[maybe_unused]] int x = 5; // suppress unused warning
[[noreturn]] void fatal(); // function never returns
[[likely]] / [[unlikely]] // branch-prediction hint (C++20)
[[fallthrough]]; // intentional switch fallthroughYou're now writing C++ that any modern team would accept. The last topic curates places to practice.