Modern C++

What Is Modern C++?

Modern C++ refers to the style and features introduced since C++11 and expanded in C++14, C++17, C++20, and C++23. It focuses on safety, expressiveness, performance, and clean design.

Smart Pointers

Smart pointers automatically manage dynamically allocated memory. They free memory when the object is no longer needed, preventing memory leaks and eliminating manual delete.

Prefer std::unique_ptr unless shared ownership is required.


#include <memory>
#include <iostream>

int main() {
    std::unique_ptr ptr = std::make_unique(42);

    std::cout << *ptr << std::endl;

} // ptr automatically deletes the memory here

The memory allocated by make_unique is automatically released when ptr goes out of scope.

Move Semantics

Move semantics let you transfer resources instead of copying them — essential for performance.


std::vector a = {1,2,3};
std::vector b = std::move(a); // moves instead of copies

Lambdas

A lambda is an anonymous inline function. They are commonly used with STL algorithms, callbacks, and short operations.

General syntax: [capture](parameters) -> return_type { body }


#include <iostream>
#include <vector>
#include <algorithm>

int main() {

    // 1. Simple lambda
    auto hello = []() {
        std::cout << "Hello\n";
    };
    hello();

    // 2. Lambda with parameters
    auto add = [](int a, int b) {
        return a + b;
    };
    std::cout << add(2,3) << std::endl;

    // 3. Capture by value
    int x = 10;
    auto show = [x]() {
        std::cout << x << std::endl;
    };

    // 4. Capture by reference
    auto modify = [&x]() {
        x++;
    };
    modify();

    // 5. Capture everything
    auto captureAll = [=]() { std::cout << x; };  // by value
    auto captureRef = [&]() { x++; };             // by reference

    // 6. Lambda with explicit return type
    auto divide = [](double a, double b) -> double {
        return a / b;
    };

    // 7. Lambda used with STL
    std::vector v = {3,1,4};
    std::sort(v.begin(), v.end(), [](int a, int b) {
        return a < b;
    });

}

Lambdas are widely used with STL algorithms like sort, for_each, and find_if.

Type Deduction (auto)

auto lets the compiler automatically determine a variable’s type from its initializer. It reduces verbosity and is commonly used with STL containers and iterators.

The type of auto is deduced at compile time from the assigned value.


#include <vector>
#include <iostream>

int main() {

    // 1. Basic type deduction
    auto x = 10;        // int
    auto y = 3.14;      // double
    auto name = "C++";  // const char*

    // 2. With references
    int a = 5;
    auto b = a;   // copy (int)
    auto& c = a;  // reference to a

    // 3. With const
    const int k = 7;
    auto m = k;        // int (const removed)
    const auto n = k;  // const int

    // 4. Iterators (very common use)
    std::vector v = {1,2,3};

    for (auto it = v.begin(); it != v.end(); ++it) {
        std::cout << *it << std::endl;
    }

    // 5. Range-based loop
    for (auto value : v) {
        std::cout << value << std::endl;
    }

}

auto is especially useful when working with complex STL types like iterators and template return values.

optional, variant, any

Modern C++ provides safer data containers for representing absent values and type-safe unions.


std::optional find_value(bool ok) {
    if (ok) return 42;
    return std::nullopt;
}

Ranges (C++20)

The ranges library allows chaining operations like filter, transform, and take into readable pipelines.


#include <vector>
#include <ranges>

std::vector vec = {1,2,3,4,5,6};

auto even = vec | std::views::filter([](int x){ return x % 2 == 0; });

The pipeline keeps only even numbers from the vector.

Threads (Multithreading)

A thread allows a program to run multiple tasks at the same time. C++ provides the <thread> library to create and manage threads.

Each thread runs a function independently from the main program.


#include <iostream>
#include <thread>

void worker() {
    std::cout << "Hello from thread\n";
}

int main() {

    std::thread t(worker); // start new thread

    t.join(); // wait for the thread to finish

}

join() blocks the main thread until the worker thread finishes.

constexpr

constexpr tells the compiler that a value or function can be evaluated at compile time. This can improve performance and enable constant expressions in places like array sizes and templates.

If all inputs are known at compile time, the result is computed during compilation.


#include <iostream>

constexpr int square(int x) {
    return x * x;
}

int main() {

    constexpr int a = square(5); // computed at compile time

    std::cout << a << std::endl;

}

Functions marked constexpr can run at compile time when given constant inputs.

Templates

Templates enable writing generic code that works with any data type. Modern C++ has significantly enhanced their power and usability.


#include <iostream>
#include <string>

// Example: Variadic Template with Fold Expression (C++17)
template
void print(Args... args) {
    // (std::cout << ... << args) is a fold expression
    // It expands to: std::cout << arg1 << arg2 << ...
    (std::cout << ... << args) << std::endl;
}

int main() {
    print("Hello", " ", 123, " ", 3.14);
    print("Only one argument");
}

Templates are crucial for building highly generic and reusable C++ libraries.

Strongly Typed Enums

enum class creates a strongly typed enumeration. Unlike traditional enum, it prevents implicit conversions and keeps enumerator names scoped.

This improves type safety and avoids name conflicts in larger programs.


#include <iostream>

enum class Color { Red, Green, Blue };

int main() {

    Color c = Color::Red;

    if (c == Color::Red) {
        std::cout << "Red selected\n";
    }

}

Enumerator names must be accessed with the scope operator: Color::Red, Color::Green, etc.