11 Lambdas & Modern Control Flow
Overview: Inline Abstractions and Type Inference
Modern C++ (C++11 and beyond) introduces Lambda Expressions and type inference via
auto. This note covers lambda functor mechanics, capture scopes, and range-based loops.
1. Lambda Expressions & Under-the-Hood Functors
A Lambda Expression allows you to define an anonymous function inline at its call site.
auto myLambda = [](int x, int y) { return x + y; };How the Compiler translates Lambdas:
A lambda is syntactic sugar for a Functor (a class with an overloaded function call operator operator()). For the lambda above, the compiler generates a unique, unnamed struct behind the scenes:
struct UnnamedLambda {
auto operator()(int x, int y) const { return x + y; }
};
UnnamedLambda myLambda; // Created at compile time2. Capture Lists Policies
The capture list [] allows you to make variables from the enclosing outer scope available inside the lambda’s block.
- Value Capture
[x]: Copies the value ofxinto a member variable of the compiler-generated struct. Modifyingxinside the lambda does not change the outer variable. - Reference Capture
[&x]: Stores a reference toxinside the struct. Modifyingxinside the lambda changes the outer variable. - Scope Value Capture
[=]: Captures all variables currently in scope by value. - Scope Reference Capture
[&]: Captures all variables currently in scope by reference.
3. Compile-time Type Inference: auto
The auto keyword tells the compiler to automatically deduce a variable’s type from its initialization expression at compile time.
auto val = 4.2; // Deduces to double
auto it = myVector.begin(); // Replaces long, complex iterator signaturesStatic Typing remains intact:
- Zero Runtime Overhead: Type deduction occurs entirely at compile time. The variable is strictly typed in the compiled machine code.
autodoes not mean dynamic typing (like Python or JavaScript). You cannot reassign anautovariable to a value of a different type after declaration.
4. Range-based For Loops
Range-based loops simplify container traversal.
for (auto elem : arr) { ... }Compilation translation:
The compiler translates a range-for loop into a traditional iterator-based loop:
for (auto it = arr.begin(); it != arr.end(); ++it) {
auto elem = *it;
...
}Avoid Unnecessary Copies
By default,
auto elemcopies each item in the container. When iterating over large structures, useconst auto& elemto pass elements by reference without copying.
💻 Conceptual Code Demonstration
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {10, 5, 20, 15};
int multiplier = 3;
// 1. Lambda capturing by value [multiplier]
// The compiler copies 'multiplier' into the generated functor struct.
auto multiplyByVal = [multiplier](int x) {
return x * multiplier;
};
std::cout << "3 * 10 = " << multiplyByVal(10) << '\n';
// 2. Pass Lambda to STL algorithm
// Sorts elements in descending order
std::sort(numbers.begin(), numbers.end(), [](int lhs, int rhs) {
return lhs > rhs;
});
// 3. Range-based For Loop using const auto& reference (avoids copies)
std::cout << "Sorted Numbers: ";
for (const auto& num : numbers) {
std::cout << num << " ";
}
std::cout << '\n';
return 0;
}