Skip to main content

Lambda Expressions

Lambdas (C++11) are anonymous functions that can capture variables from surrounding scope, enabling functional programming patterns and convenient callbacks.

Syntactic Sugar

Lambdas are compiler-generated function objects (functors). The lambda [](){} creates an unnamed class with operator().

Basic Syntax

// Minimal lambda
auto lambda = []() {
std::cout << "Hello\n";
};

lambda(); // Call it

// With parameters and return
auto add = [](int a, int b) {
return a + b;
};

int result = add(5, 3); // 8

Parts:

[capture](parameters) -> return_type { body }
^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^ ^^^^
capture params return type code
clause (optional)

Captures

No Capture

auto lambda = []() { return 42; };
// Cannot access outer variables

Capture by Value

int x = 10;
auto lambda = [x]() {
return x * 2; // Copy of x
};

x = 20;
std::cout << lambda(); // 20 (uses old value of x)

Capture by Reference

int x = 10;
auto lambda = [&x]() {
x *= 2; // Modifies original x
};

lambda();
std::cout << x; // 20

Multiple Captures

int x = 1, y = 2;

[x, y]() { } // Capture x and y by value
[&x, &y]() { } // Capture x and y by reference
[x, &y]() { } // x by value, y by reference
[=]() { } // Capture all by value
[&]() { } // Capture all by reference
[=, &x]() { } // All by value except x by reference
[&, x]() { } // All by reference except x by value

Init Capture (C++14)

int x = 10;

// Create new variable in capture
auto lambda = [y = x * 2]() {
return y; // y = 20
};

// Move capture
auto ptr = std::make_unique<int>(42);
auto lambda2 = [p = std::move(ptr)]() {
return *p; // Owns the unique_ptr
};

Parameters and Return Types

Auto Parameters (C++14)

// Generic lambda
auto print = [](auto x) {
std::cout << x << "\n";
};

print(42); // Works with int
print(3.14); // Works with double
print("hello"); // Works with const char*

Explicit Return Type

// Return type deduced
auto add = [](int a, int b) { return a + b; }; // Returns int

// Explicit return type
auto divide = [](int a, int b) -> double {
return static_cast<double>(a) / b;
};

mutable Keyword

By default, captured-by-value variables are const:

int x = 10;

auto lambda1 = [x]() {
// x = 20; // ❌ Error: x is const
return x;
};

auto lambda2 = [x]() mutable {
x = 20; // ✅ OK: modifies copy
return x;
};

lambda2();
std::cout << x; // 10 (original unchanged)

Common Use Cases

STL Algorithms

std::vector<int> vec = {1, 2, 3, 4, 5, 6};

// Count even numbers
int count = std::count_if(vec.begin(), vec.end(),
[](int n) { return n % 2 == 0; });

// Transform
std::transform(vec.begin(), vec.end(), vec.begin(),
[](int n) { return n * 2; });

// Custom sort
std::sort(vec.begin(), vec.end(),
[](int a, int b) { return a > b; }); // Descending

Callbacks

void process(std::function<void()> callback) {
// Do work...
callback();
}

int result = 0;
process([&result]() {
result = 42;
});

RAII Guards

{
auto guard = [cleanup = some_resource]() {
// Cleanup happens when guard destroyed
};

// ... use resource ...

} // Guard destroyed, cleanup happens

Lambda as Function Object

Lambdas are functors - classes with operator():

// Lambda
auto lambda = [x = 10](int y) { return x + y; };

// Equivalent class
class Lambda {
int x;
public:
Lambda(int x_) : x(x_) {}
int operator()(int y) const { return x + y; }
};
Lambda lambda2(10);

Recursive Lambdas

Requires std::function or auto with explicit type:

// Using std::function
std::function<int(int)> factorial = [&factorial](int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
};

// C++23: Explicit object parameter (deducing this)
auto factorial = [](this auto&& self, int n) {
return n <= 1 ? 1 : n * self(n - 1);
};

Immediately Invoked Lambda

// IIFE (Immediately Invoked Function Expression)
int value = []() {
int temp = expensive_computation();
return temp * 2;
}(); // Called immediately

// Useful for const initialization
const auto data = [&]() {
std::vector<int> v;
// Complex initialization
return v;
}();

Template Lambdas (C++20)

// Template lambda
auto lambda = []<typename T>(T value) {
return value * 2;
};

auto x = lambda(5); // int
auto y = lambda(3.14); // double

Captures and Object Lifetime

// ❌ Dangling reference
std::function<int()> makeFunc() {
int x = 42;
return [&x]() { return x; }; // ❌ x destroyed!
}

// ✅ Capture by value
std::function<int()> makeFunc() {
int x = 42;
return [x]() { return x; }; // ✅ Copy of x
}

// ✅ Init capture with move
std::function<int()> makeFunc() {
auto ptr = std::make_unique<int>(42);
return [p = std::move(ptr)]() { return *p; };
}

Performance Considerations

// Stack-allocated (fast)
auto lambda = [](int x) { return x * 2; };

// std::function (slower, heap allocation possible)
std::function<int(int)> func = [](int x) { return x * 2; };

// Template parameter (fastest, inlined)
template<typename Func>
void process(Func f) {
f(42);
}

process([](int x) { std::cout << x; }); // No overhead

Guideline: Use auto for lambdas, std::function only when type erasure needed.


Best Practices

DO
  • Use auto for lambda variables
  • Prefer capture by value for small types
  • Use init capture for move-only types
  • Mark non-modifying lambdas const (default)
DON'T
  • Capture by reference if lambda outlives scope
  • Use [=] when you mean specific captures
  • Forget mutable when modifying captured values
  • Over-use std::function (performance cost)

Summary

Lambda syntax:

[captures](params) -> return_type { body }

Captures:

  • [x] - Value
  • [&x] - Reference
  • [=] - All by value
  • [&] - All by reference
  • [y = x] - Init capture (C++14)

Common patterns:

// STL algorithm
std::sort(vec.begin(), vec.end(), [](int a, int b) { return a > b; });

// Callback
button.onClick([this]() { handleClick(); });

// IIFE
const auto x = []() { return compute(); }();

// Generic lambda (C++14)
auto print = [](const auto& x) { std::cout << x; };

Use when:

  • Inline callbacks
  • STL algorithms
  • Short-lived function objects
  • Functional programming patterns

Lambdas make C++ more expressive, enabling clean functional-style code while maintaining zero-overhead abstraction principles.