Lambda Expressions: Anonymous Functions
C++11 introduced lambdas: inline anonymous function objects with concise syntax. Lambdas are extensively used with algorithms, callbacks, and asynchronous operations.
auto sum = [](int a, int b) { return a + b; };
std::cout << sum(3, 4); // Outputs: 7
std::vector<int> v = {4, 2, 5, 1, 3};
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });
// v is now {5, 4, 3, 2, 1}
Lambda Syntax
[captures](parameters) specifiers -> return_type { body }
- captures: Variables from enclosing scope
- parameters: Function parameters (optional, default
()) - specifiers:
mutable,constexpr,noexcept - return_type: Can be deduced or explicit
- body: Function implementation
Minimal Lambda
auto f = [] { return 42; }; // No parameters, deduced return type
Capture Modes
Lambdas can capture variables from their enclosing scope:
Capture by Value
int x = 10;
auto lambda = [x]() { return x * 2; }; // Captures x by value
x = 20;
std::cout << lambda(); // Outputs: 20 (captured value at lambda creation)
Capture by Reference
int x = 10;
auto lambda = [&x]() { return x * 2; }; // Captures x by reference
x = 20;
std::cout << lambda(); // Outputs: 40 (current value of x)
Mixed Captures
int x = 1, y = 2, z = 3;
auto lambda = [x, &y, z]() {
// x and z by value, y by reference
};
Default Captures
int a = 1, b = 2;
auto lambda1 = [=]() { return a + b; }; // Capture all by value
auto lambda2 = [&]() { return a + b; }; // Capture all by reference
auto lambda3 = [=, &b]() { return a + b; }; // All by value except b
auto lambda4 = [&, a]() { return a + b; }; // All by reference except a
Warning: Capturing [=] doesn’t capture this in member functions (C++17 changed this). Prefer explicit captures.
Mutable Lambdas
By default, value-captured variables are const. Use mutable to modify them:
int x = 0;
auto counter = [x]() mutable {
return ++x; // Modifies lambda's copy of x
};
std::cout << counter(); // 1
std::cout << counter(); // 2
std::cout << x; // 0 (original x unchanged)
Generic Lambdas (C++14)
Parameters can use auto for generic types:
auto print = [](const auto& x) {
std::cout << x << '\\n';
};
print(42); // Works with int
print(3.14); // Works with double
print("hello"); // Works with const char*
Equivalent to a template function:
struct Lambda {
template<typename T>
void operator()(const T& x) const {
std::cout << x << '\\n';
}
};
Init Captures (C++14)
Capture with initialization, enabling move-only types:
auto ptr = std::make_unique<int>(42);
auto lambda = [p = std::move(ptr)]() {
return *p;
};
// ptr is now nullptr, lambda owns the unique_ptr
Rename and transform during capture:
int x = 5;
auto lambda = [y = x * 2]() {
return y; // y is 10
};
Return Type Deduction
Usually automatic, but can be explicit:
auto lambda1 = [](int x) { return x * 2; }; // Deduced: int
auto lambda2 = [](int x) -> double { // Explicit: double
return x * 2.5;
};
Required when return type is ambiguous:
auto lambda = [](bool flag) {
if (flag)
return 1; // int
else
return 2.5; // double - ERROR: inconsistent types
};
// Fix with explicit return type:
auto lambda = [](bool flag) -> double {
if (flag) return 1;
else return 2.5;
};
Lambda Type and std::function
Each lambda has a unique compiler-generated type. Use auto to store:
auto lambda = [](int x) { return x * 2; };
std::function provides type erasure but has overhead:
#include <functional>
std::function<int(int)> func = [](int x) { return x * 2; };
// More flexible but slower than auto
Prefer auto unless you need type erasure or reassignment.
Immediately Invoked Lambda Expression (IIFE)
Useful for complex initialization:
const auto value = [&]() {
if (condition1) return compute1();
else if (condition2) return compute2();
else return default_value();
}(); // Immediately invoked
Lambdas with Algorithms
Classic use case: predicates and projections
std::vector<int> v = {1, 2, 3, 4, 5};
// Remove odd numbers
v.erase(std::remove_if(v.begin(), v.end(),
[](int x) { return x % 2 != 0; }),
v.end());
// Count elements > 3
auto count = std::count_if(v.begin(), v.end(),
[](int x) { return x > 3; });
What does [=] capture in a member function (C++17+)?
Interactive Lab
Waiting for signal...