Lambda Expressions
A lambda expression in C++11 follows the syntax: [capture-list] (parameters) mutable -> return-type { statement }.
-
Capture List (
[capture-list]): Defines variables accessible within the lambda. It must be present and is used by the compiler to identify a lambda. Variables can be captured by value or by reference.[v]: Captures variablevby value.[=]: Captures all variables from the enclosing scope by value, includingthis.[&v]: Captures variablevby reference.[&]: Captures all variables from the enclosing scope by reference, includingthis.[this]: Captures the currentthispointer by value.
-
Parameter List
(parameters): Optional. Matches standard function parameters. Can be omitted if no arguments are needed. -
mutable: Allows modification of captured variables even if they were captured by value. This keyword requires the parameter list to be explicitly writtan, even if empty.
-
Return Type (
-> return-type): Optional. The compiler can deduce the return type usingauto. Explicit declaration is only necessary when the return type cannot be inferred. -
Function Body
{statement}: Required. Contains the logic executed when the lambda is invoked. Accesses both parameters and captured variables.
Note: The capture list and function body are mandatory, while other parts may be omitted. The minimal valid lambda is
[] {}.
Example Usage
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> nums = {1, 3, 5, 7, 2, 4, 6, 8};
int x = 3, y = 4;
// Capture by value
auto add_three = [=]() { return x + 3; };
std::cout << add_three() << "\n";
// Capture by reference
auto modify_y = [&](int val) { y += val; };
modify_y(10);
std::cout << "x: " << x << ", y: " << y << "\n";
// Full lambda with return type
auto sum_with_x = [=, &y](int z) -> int { return y += x + z; };
std::cout << sum_with_x(10) << "\n";
// Mutable capture
int num = 10;
auto double_and_add = [num](int input) mutable {
num *= 2;
return input + num;
};
std::cout << double_and_add(10) << "\n";
// Sorting with lambda
std::sort(nums.begin(), nums.end(), [](int a, int b) { return a > b; });
for (const auto& n : nums) {
std::cout << n << " ";
}
std::cout << "\n";
return 0;
}
Lambda expressions are essentially unnamed function objects. The compiler generates a unique class for each lambda, overloading operator() to enable invocation. Despite identical bodies, lambdas have distinct types and cannot be assigned to one another.
Function Wraper (std::function)
std::function is a general-purpose polymorphic function wrapper that enables uniform handling of various callable entities such as function pointers, functors, and lambdas.
#include <functional>
#include <iostream>
// Regular function
double divide_by_two(double d) {
return d / 2;
}
// Functor
struct Divider {
double operator()(double d) const {
return d / 3;
}
};
// Generic wrapper function
template <class F, class T>
T execute(F func, T arg) {
static int call_count = 0;
std::cout << "Call #" << ++call_count << " (address: " << &call_count << ")\n";
return func(arg);
}
int main() {
// Using function pointer
std::function<double(double)> wrapper1 = divide_by_two;
std::cout << execute(wrapper1, 11.11) << "\n";
// Using functor
std::function<double(double)> wrapper2 = Divider();
std::cout << execute(wrapper2, 11.11) << "\n";
// Using lambda
std::function<double(double)> wrapper3 = [](double d) { return d / 4; };
std::cout << execute(wrapper3, 11.11) << "\n";
return 0;
}
For member functions, special care is required:
int subtract(int a, int b) { return a - b; }
class Calculator {
public:
static int add_static(int a, int b) { return a + b; }
double subtract_member(double a, double b) { return a - b; }
};
int main() {
// Non-member function
std::function<int(int, int)> f1 = subtract;
std::cout << f1(10, 5) << "\n";
// Static member function
std::function<int(int, int)> f2 = &Calculator::add_static;
std::cout << f2(1, 1) << "\n";
// Non-static member function
Calculator calc;
std::function<double(Calculator*, double, double)> f3 = &Calculator::subtract_member;
std::cout << f3(&calc, 10, 5) << "\n";
std::function<double(Calculator, double, double)> f4 = &Calculator::subtract_member;
std::cout << f4(Calculator(), 10, 5) << "\n";
return 0;
}
std::bind – Parameter Binding
std::bind allows fixing certain arguments of a functon and rearranging parameter order.
Reordering Parameters
int difference(int a, int b) { return a - b; }
int main() {
int x = 10, y = 20;
std::cout << difference(x, y) << "\n"; // Output: -10
auto reversed = std::bind(difference, std::placeholders::_2, std::placeholders::_1);
std::cout << reversed(x, y) << "\n"; // Output: 10
return 0;
}
Fixing Arguments
class MathOp {
public:
double compute(double a, double b) { return a - b; }
};
int main() {
// Bind two arguments
std::function<double(double, double)> op1 = std::bind(&MathOp::compute, MathOp(), std::placeholders::_1, std::placeholders::_2);
std::cout << op1(2.0, 3.0) << "\n"; // Output: -1.0
// Fix second argument
std::function<double(double)> op2 = std::bind(&MathOp::compute, MathOp(), std::placeholders::_1, 20.0);
std::cout << op2(2.0) << "\n"; // Output: -18.0
return 0;
}
std::bind is especially useful when creating function objects with pre-bound parameters, enabling partial application and reusable callbacks.