Home | Projects | Notes > C++ Programming > Stateless Lambda Expressions
Stateless lambda expressions are lambdas that do not capture any external variables from their surrounding scope. An empty capture list means that the expression captures no information from its environment and only has access to the data passed through its function parameter list.
31[] () { std::cout << "Hi"; } ();        // Displays Hi2int x{10};3[] (int x) { std::cout << x; }(100);L3: This lambda is invoked with the value
100passed to itsxparameter. Since it is stateless, it captures no information from its surrounding environment. This means it has no access to thexvariable defined earlier and only operates on the value explicitly passed to its parameter when it is called.
121const int n{3};2int num[n]{10, 20, 30};3
4auto sum = [] (int nums[], int n)5{6    int sum{0};7    for (int i = 0; i < n; i++)8        sum += nums[i];9    return sum;10};11
12std:: cout << sum(nums, 3);     // Displays 60L4: From the empty capture list, we know that this lambda is stateless, meaning it has no access to the array or its length defined earlier. The only way it can compute the sum of the integers in the array is if both the array and its length are passed as parameters. This is exactly what happens when the lambda is called in L12.
21[] (int x) { std::cout << x; };2[] (int &x) { std::cout << x; };L1:
int x- Value parameterL2:
int &x)- Reference parameter (i.e., alias to the actual parameter; no copy is made)
131int test_score1{88};2int test_score2{75};3
4auto bonus = [] (int &score1, int &score2, int bonus_points)5{6    score1 += bonus_points;7    score2 += bonus_points;8};9
10bonus(test_score1, test_score2, 5);11
12std::cout << "test_score1: " << test_score1 << std::endl;   // Displays 9313std::cout << "text_score2: " << test_score2 << std::endl;   // Displays 8031int x;2auto l = [] (int *x) { std::cout << *x; }; // '*': dereferencing operator3l(&x);  // '&': Referencing (address-of) operator131int test_score1{88};2int test_score2{75};3
4auto bonus = [] (int *score1, int *score2, int bonus_points)5{6    *score1 += bonus_points;7    *score2 += bonus_points;8};9
10bonus(&test_score1, &test_score2, 5);11
12std::cout << "test_score1: " << test_score1 << std::endl;   // Displays 9313std::cout << "text_score2: " << test_score2 << std::endl;   // Displays 80161std::vector<int> test_scores{93, 88, 75, 68, 65};2
3auto bonus = [] (std::vector<int> &scores, int bonus_points)4{5    for (int &score : scores)6        score += bonus_points;7};8
9bonus(test_scores, 5);10
11std::cout << "test_scores: " << std::endl;12std::cout << text_scores[0] << std::endl;   // Displays 9813std::cout << text_scores[1] << std::endl;   // Displays 9314std::cout << text_scores[2] << std::endl;   // Displays 8015std::cout << text_scores[3] << std::endl;   // Displays 7316std::cout << text_scores[4] << std::endl;   // Displays 70auto as Lambda Parameter Type SpecifiersThis allows the lambda expression to accommodate different types of arguments, making it more flexible and enabling it to work like a generic function. (The auto keyword is the key!)
Note: auto is not an actual type. It's an instruction telling the compiler to deduce the actual type.
71int num1{10};2float num2 {20.5};3
4auto l = [] (auto x) { std::cout << x; };5
6l(num1);7l(num2);111std::vector<int> test_scores1 {93, 88, 75, 68, 65 };2std::vector<float> test_scores2 {88.5, 85.5, 75.5, 68.5, 65.5};3
4auto bonus = [] (auto &scores, int bonus_points)5{6    for (auto &score : scores)7        score += bonus_points;8};9
10bonus(test_scores1, 5);     // Valid11bonus(test_scores2, 5);     // Valid61// For std::function2
3void foo(std::function<void(int)> l) { l(10); }     // C++144void foo(void (*)(int)) { l(10); }                  // C++145
6void foo(auto l) { l(10); }                         // C++20L3: Passing a lambda expression to a function as a function object using the standard library's
<functional>header. The functionfootakes the function objectlas a function parameter.voidtype specifier represents the return type of the function object, and theinttype specifier represents the function object's parameter type. (C++14)L4: Passing a lambda expression to a function as a function pointer. The function
footakes as its parameter a pointer to the functionl. (C++14)L6: In C++20, we can eliminate the need to explicitly declare return and parameter types by using the
autokeyword, allowing the compiler to deduce both the parameter types and the return type of the lambda expression.
Similar to how the lambda expressions are passed to functions, they can be returned as either function objects, function pointers or by using the auto keyword to instruct the compiler to deduce the return type.
61// For std::function2
3std::function<void(int)> foo() { return [] (int x) { std::cout << x; }; }4void (*foo())(int) { return [] (int x) { std::cout << x; }; }5
6auto foo() { return [] (int x) { std::cout << x; }; }L3: Returning a lambda expression as a function object.
L4: Returning a lambda expression as a function pointer. This is an old-style C syntax that has persisted for backward compatibility. In modern C++, it's uncommon to use this approach to return a lambda expression from a function. Instead, it's more typical—and more flexible—to return lambdas either as function objects or by using the
autokeyword.
All 3 versions are used the same way:
21auto l = foo();2l(10);      // Displays 10Examples of why you might want to return a lambda from a function are best illustrated using stateful lambda expressions, which will be discussed in the next section.
41foo([] (int x) { std::cout << x; });2
3auto l = [] (int x) { std::cout << x; };4foo(l);L1: A common way of passing lambda expressions to functions since in most cases they're only ever passed once.
L3: If the lambda will be used more than once, it may be beneficial to assign it to a variable so that it can be passed to multiple functions and called independently without having to define the lambda each time.
A predicate in C++ is a function that takes one or more arguments and returns a boolean value. Naturally, a predicate lambda is a lambda expression that implements this behavior. This is where the true power of lambdas shines—enabling concise, inline logic for filtering, searching, and decision-making in algorithms.
181void print_if(std::vector<int> nums, bool (*predicate)(int))2{3    for (int i : nums)4    {5        if (predicate(i))6            std::cout << i;7    }8}9
10int main()11{12    std::vector<int> nums{1, 2, 3};13    14    print_if(nums, [] (auto x) { return x % 2 == 0; }); // Displays evens15    print_if(nums, [] (auto x) { return x % 2 != 0; }); // Displays odds16    17    return 0;18}L1: Takes as its parameters an integer vector and a predicate lambda that's passed as a function pointer. In this case, the predicate lambda is used to determine which elements of the integer vector to display.
Predicate lambdas are especially important when working with Standard Template Library (STL) functions and algorithms such as std::sort() or std::for_each(), which often take a predicate as a parameter to customize their behavior.