Lesson 14 / 25
Iterators, Algorithms, Lambdas and Ranges
Use standard algorithms with lambdas and C++20 ranges instead of hand-written loops.
Say what, not how
The <algorithm> and <numeric> headers provide well-tested generic algorithms: std::sort, std::stable_sort, std::find, std::find_if, std::count_if, std::binary_search, std::lower_bound, std::transform, std::accumulate, std::remove_if, std::unique, std::max_element and many more. Classic algorithms take a pair of iterators marking a half-open range [begin, end). Lambdas ([](int x) { return x > 50; }) supply custom behaviour inline; the capture list controls access to surrounding variables by value ([threshold]) or reference ([&total]). C++20 ranges (<ranges>) let you pass whole containers (std::ranges::sort(v)) and compose lazy views with the pipe operator: v | std::views::filter(pred) | std::views::transform(f). Algorithms express intent clearly, avoid off-by-one errors and are often optimised. Note the erase-remove idiom for vectors before C++20 (v.erase(std::remove_if(...), v.end())), replaced in C++20 by std::erase_if(v, pred).
Algorithms and ranges instead of loops
Each line replaces a hand-written loop.
#include <algorithm>
#include <numeric>
#include <ranges>
#include <string>
#include <vector>
struct Student { std::string name; int marks; };
int main() {
std::vector<Student> cls{{"Asha", 91}, {"Ravi", 64}, {"Meera", 78}, {"Kabir", 38}};
std::ranges::sort(cls, std::ranges::greater{}, &Student::marks); // by marks, descending
int threshold = 60;
auto passed = std::ranges::count_if(cls, [threshold](const Student& s) { return s.marks >= threshold; });
double avg = std::accumulate(cls.begin(), cls.end(), 0.0,
[](double sum, const Student& s) { return sum + s.marks; }) / cls.size();
auto top_names = cls
| std::views::filter([](const Student& s) { return s.marks >= 75; })
| std::views::transform(&Student::name); // lazy view
for (const auto& n : top_names) { /* Asha, Meera */ }
std::erase_if(cls, [](const Student& s) { return s.marks < 40; }); // C++20
std::vector<int> sorted{3, 8, 15, 21, 40};
bool has15 = std::ranges::binary_search(sorted, 15); // O(log n)
}Kitchen appliances
Writing every loop by hand is chopping everything with a knife. Standard algorithms are a well-tested food processor with labelled settings: sort, filter, count. You still choose the recipe (the lambda).
Quick check: What does the capture list `[&total]` in a lambda mean?
- The lambda receives total as a parameter
- The lambda copies total
- total becomes a global variable
- The lambda captures total by reference, so changes affect the original variable
Answer
The lambda captures total by reference, so changes affect the original variable — & captures by reference; without & the variable would be copied into the lambda.