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.
#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);
}#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;
}#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.