Lesson 12 of 13

Modern C++: Lambdas, Algorithms & Ranges

Learn , the standard and ranges, which let you say what you want done instead of writing index by hand.

C++
#include <algorithm>
#include <numeric>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> nums{5, 3, 9, 1, 7, 3};

    // Lambdas: [captures](params) { body }
    auto isOdd = [](int n) { return n % 2 != 0; };

    int threshold = 4;
    auto above = [threshold](int n) { return n > threshold; };   // capture by value
    int count = 0;
    auto tally = [&count](int) { ++count; };                     // capture by reference
    auto everything = [&](int n) { return n > threshold; };      // capture all by ref
    auto mutableLambda = [x = 0](int n) mutable { return x += n; };

    std::sort(nums.begin(), nums.end());
    std::sort(nums.begin(), nums.end(), std::greater<>{});       // descending

    auto it = std::find_if(nums.begin(), nums.end(), above);
    if (it != nums.end()) std::cout << "first > 4: " << *it << "\n";

    std::cout << std::count_if(nums.begin(), nums.end(), isOdd) << "\n";
    std::cout << std::accumulate(nums.begin(), nums.end(), 0) << "\n";
    std::cout << std::any_of(nums.begin(), nums.end(), above) << "\n";

    // erase-remove idiom: actually delete the matching elements
    nums.erase(std::remove_if(nums.begin(), nums.end(), isOdd), nums.end());
    // C++20 shortcut: std::erase_if(nums, isOdd);
}
C++
#include <ranges>
#include <vector>
#include <string>
#include <iostream>

int main() {
    std::vector<int> nums{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    // C++20 ranges: composable views, evaluated lazily, no temporary vectors
    auto result = nums
        | std::views::filter([](int n) { return n % 2 == 0; })
        | std::views::transform([](int n) { return n * n; })
        | std::views::take(3);

    for (int n : result) std::cout << n << ' ';    // 4 16 36
    std::cout << '\n';

    // Algorithms without .begin()/.end() noise
    std::ranges::sort(nums);
    auto pos = std::ranges::find(nums, 7);
    std::cout << std::ranges::count_if(nums, [](int n) { return n > 5; }) << "\n";

    // Common views
    for (int n : std::views::iota(1, 5)) std::cout << n;          // 1234
    for (int n : nums | std::views::reverse | std::views::take(2)) std::cout << n;
}
C++
#include <optional>
#include <variant>
#include <string>
#include <string_view>
#include <iostream>

// optional<T>: a value that may be absent, without using a sentinel or a pointer
std::optional<int> parsePort(const std::string& text) {
    try {
        int value = std::stoi(text);
        if (value < 1 || value > 65535) return std::nullopt;
        return value;
    } catch (...) {
        return std::nullopt;
    }
}

// variant<A, B>: exactly one of several types, checked at compile time
using Result = std::variant<int, std::string>;

std::string render(const Result& r) {
    return std::visit([](const auto& v) -> std::string {
        if constexpr (std::is_same_v<std::decay_t<decltype(v)>, int>)
            return "ok: " + std::to_string(v);
        else
            return "error: " + v;
    }, r);
}

// string_view: a non-owning window into a string — no allocation, no copy
void logLine(std::string_view line) { std::cout << line << '\n'; }

int main() {
    if (auto port = parsePort("8080")) std::cout << *port << "\n";
    std::cout << parsePort("abc").value_or(80) << "\n";          // 80

    std::cout << render(Result{42}) << "\n";
    std::cout << render(Result{std::string("bad input")}) << "\n";

    std::string s = "hello world";
    logLine(s);
    logLine("a literal");        // no std::string is constructed
}
Finished reading?Mark it done to track your progress.

Tip: use the ← and → keys to move between lessons.