Lambda Functions in C++


Lambda Functions in C++ Interview with follow-up questions

1. What is a lambda function in C++?

A lambda function in C++ is an anonymous, inline function object (a closure) that can capture variables from its surrounding scope. Lambdas were introduced in C++11 and have grown significantly more powerful in C++14, C++17, C++20, and C++23.

The general syntax is:

[capture](parameters) specifiers -> return_type { body }

A simple example:

auto square = [](int x) { return x * x; };
std::cout << square(5);  // 25

The compiler generates an unnamed class with an operator() for each lambda — lambdas are syntactic sugar for callable objects, not raw function pointers.

Key components:

  • Capture list []: specifies which variables from the enclosing scope the lambda can use (by value =, by reference &, or named captures).
  • Parameter list: same as a regular function; can use auto for generic lambdas (C++14+).
  • Specifiers: mutable allows modifying captured-by-value variables; noexcept marks the call operator as non-throwing; consteval/constexpr (C++17/20) allow compile-time evaluation.
  • Return type: usually deduced; can be explicit with -> T.

C++20 additions worth mentioning in interviews:

  • Template lambdas: [](T x) { return x; } — explicit type parameters for generic lambdas.
  • Lambdas in unevaluated contexts (e.g., decltype).
  • [=] no longer captures this implicitly — you must write [=, this] explicitly.

Lambdas are the idiomatic way to pass behaviour to standard algorithms (std::sort, std::transform, std::ranges::filter) and are essential for writing concise, readable C++ today.

↑ Back to top

Follow-up 1

Can you explain with an example?

Sure! Here's an example of a lambda function in C++:

#include 
#include 
#include 

int main() {
    std::vector numbers = {1, 2, 3, 4, 5};

    // Using a lambda function to multiply each number by 2
    std::transform(numbers.begin(), numbers.end(), numbers.begin(), [](int n) { return n * 2; });

    // Printing the modified numbers
    for (int number : numbers) {
        std::cout << number << " ";
    }

    return 0;
}

Output:

2 4 6 8 10

Follow-up 2

What is the syntax of a lambda function?

The syntax of a lambda function in C++ is as follows:

[capture_list](parameters) -> return_type { function_body }
  • capture_list: It is an optional list of variables that the lambda function can capture from the surrounding scope. It can be used to access variables outside the lambda function.
  • parameters: It is an optional list of parameters that the lambda function can accept.
  • return_type: It is the return type of the lambda function. If not specified, the return type is automatically deduced by the compiler.
  • function_body: It is the body of the lambda function, which contains the code to be executed.

Follow-up 3

What are the benefits of using lambda functions?

There are several benefits of using lambda functions in C++:

  1. Concise code: Lambda functions allow you to write shorter and more readable code by eliminating the need for defining separate functions.
  2. Flexibility: Lambda functions can be defined inline within a code block, making it easier to express small and simple operations without cluttering the code with additional function definitions.
  3. Capturing variables: Lambda functions can capture variables from the surrounding scope, allowing you to use them within the lambda function.
  4. Standard algorithms: Lambda functions are often used with standard algorithms like std::sort, std::transform, etc., to provide custom behavior without the need for defining separate function objects.
  5. Performance: Lambda functions can sometimes improve performance by avoiding the overhead of function calls, especially when used with algorithms that can be optimized by the compiler.

2. How can you capture variables in a lambda function?

Variables from the enclosing scope are captured via the capture list [...] at the start of the lambda. There are several capture modes:

Capture by value (= or named) — the lambda gets its own copy at the point of creation:

int x = 10;
auto f = [x]() { return x * 2; };  // captures x by value
x = 99;
f();  // still returns 20 — captured the original value

Capture by reference (& or named &var) — the lambda refers to the original variable:

int count = 0;
auto inc = [&count]() { ++count; };
inc(); inc();
// count == 2

Default captures — apply to all implicitly used variables:

[=]   // capture everything by value
[&]   // capture everything by reference
[=, &x]  // everything by value except x (by reference)
[&, y]   // everything by reference except y (by value)

Capturing this in a member function:

// C++17 and earlier
auto f = [this]() { return member_; };

// C++20 — [=] no longer captures this implicitly; must be explicit:
auto f = [=, this]() { return member_; };

// C++17+ — capture a copy of *this (safe for async use):
auto f = [*this]() { return member_; };

mutable lambdas — by default, captured-by-value variables are const inside the lambda. mutable removes that restriction:

int x = 0;
auto f = [x]() mutable { x += 1; return x; };
f(); // returns 1 — modifies the lambda's own copy, not the original

C++14 init-captures (generalized capture) — create a new variable in the capture, useful for move-only types:

auto ptr = std::make_unique(42);
auto f = [p = std::move(ptr)]() { return *p; };
// ptr is now null; f owns the unique_ptr

Common interview pitfall: capturing a local variable by reference and then returning or storing the lambda past the variable's lifetime causes a dangling reference. Prefer capture by value or [*this] when the lambda may outlive the local scope.

↑ Back to top

Follow-up 1

What is the difference between capture by value and capture by reference?

The difference between capture by value and capture by reference in a lambda function is how the captured variables are accessed and modified. When capturing by value, a copy of the variable is made, and any modifications made inside the lambda function do not affect the original variable. When capturing by reference, the lambda function directly accesses and modifies the original variable.

Capture by value is useful when you want to preserve the value of the variable at the time the lambda function is created, while capture by reference is useful when you want to modify the original variable or have multiple lambda functions share the same variable.

It's important to note that when capturing by reference, you need to ensure that the captured variable remains valid for the lifetime of the lambda function. If the captured variable goes out of scope, accessing it inside the lambda function will result in undefined behavior.

Follow-up 2

Can you provide an example of each?

Sure! Here are examples of capturing variables by value and by reference:

int x = 10;

// Capture by value
auto lambda1 = [=]() {
    // use x
    x = 20; // This does not affect the original x
};

// Capture by reference
auto lambda2 = [&]() {
    // use x
    x = 20; // This modifies the original x
};

3. Can lambda functions be used with standard algorithms in C++?

Yes — lambdas and standard algorithms are designed to work together. Most algorithms in and accept a callable (function pointer, functor, or lambda) as a predicate or transformation argument. Lambdas are the most concise and readable way to supply that callable inline.

Classic `` usage:

#include 
#include 
#include 

std::vector v{5, 1, 4, 2, 3};

// Sort descending
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });

// Filter: count elements greater than 3
int cnt = std::count_if(v.begin(), v.end(), [](int x) { return x > 3; });

// Transform: square each element
std::transform(v.begin(), v.end(), v.begin(), [](int x) { return x * x; });

// Print
std::for_each(v.begin(), v.end(), [](int x) { std::cout << x << ' '; });

C++20 Ranges make this even cleaner by removing the need to pass iterator pairs and by supporting lazy, composable pipelines:

#include 
#include 

std::vector v{1, 2, 3, 4, 5, 6};

// Lazy pipeline: filter evens, square them, print
for (int x : v | std::views::filter([](int n) { return n % 2 == 0; })
               | std::views::transform([](int n) { return n * n; })) {
    std::cout << x << ' ';   // 4 16 36
}

Ranges algorithms also accept the container directly instead of iterators:

std::ranges::sort(v, [](int a, int b) { return a > b; });
std::ranges::count_if(v, [](int x) { return x > 3; });

Why lambdas are preferred over named functors here: they are defined at the point of use, keep the intent local and readable, and are often inlined by the compiler — producing code as efficient as hand-written loops.

↑ Back to top

Follow-up 1

Can you provide an example of using a lambda function with a standard algorithm?

Certainly! Here's an example of using a lambda function with the std::transform algorithm to square each element of a vector:

#include 
#include 
#include 

int main() {
    std::vector numbers = {1, 2, 3, 4, 5};
    std::vector squaredNumbers;

    std::transform(numbers.begin(), numbers.end(), std::back_inserter(squaredNumbers), [](int n) {
        return n * n;
    });

    for (int num : squaredNumbers) {
        std::cout << num << ' ';
    }

    return 0;
}

Output:

1 4 9 16 25

Follow-up 2

What are the benefits of using lambda functions with standard algorithms?

Using lambda functions with standard algorithms offers several benefits:

  1. Readability: Lambda functions allow you to define the operation or comparison logic inline, making the code more concise and easier to understand.

  2. Flexibility: Lambda functions can capture variables from their surrounding scope, allowing you to use and modify variables from the enclosing function within the lambda.

  3. Customization: Lambda functions provide a way to customize the behavior of standard algorithms by defining custom operations or comparison functions.

  4. Code reuse: Lambda functions can be used as arguments to multiple algorithms, allowing you to reuse the same logic in different contexts.

Overall, using lambda functions with standard algorithms can lead to more expressive and maintainable code.

4. What is a generic lambda and how is it used in C++?

A generic lambda is a lambda whose parameter types are deduced, making it work across multiple types without specifying each one explicitly. It was introduced in C++14 using auto parameters.

C++14 generic lambda (auto parameters):

auto multiply = [](auto x, auto y) { return x * y; };

multiply(3, 4);        // int * int = 12
multiply(2.5, 4.0);   // double * double = 10.0
multiply(2, 3.5f);    // int * float = 7.0f

Under the hood, the compiler generates a template operator() inside the closure class — one instantiation per unique combination of argument types.

C++20 template lambdas give explicit control over type parameters, enabling relationships between parameters that auto cannot express:

// Require both parameters to be the same type
auto add = [](T a, T b) { return a + b; };

add(1, 2);      // OK — both int
// add(1, 2.0); // Error — deduction conflict

// Constrain with Concepts
auto sum_range = [](const R& r) {
    return std::ranges::fold_left(r, 0, std::plus{});
};

Practical uses:

// Generic comparator for sorting any comparable type
auto cmp = [](const auto& a, const auto& b) { return a < b; };
std::sort(v.begin(), v.end(), cmp);

// Generic printer
auto print = [](const auto& val) { std::cout << val << '\n'; };
print(42);
print("hello");
print(3.14);

Interview note: Generic lambdas (C++14+) are the idiomatic replacement for writing separate named function template objects. C++20 template lambdas further close the gap between lambdas and full function templates, allowing constraints, requires clauses, and explicit template argument lists.

↑ Back to top

Follow-up 1

Can you provide an example of a generic lambda?

Sure! Here's an example of a generic lambda that adds two numbers:

auto addLambda = [](auto x, auto y) { return x + y; };

Follow-up 2

What are the benefits of using generic lambdas?

There are several benefits of using generic lambdas in C++:

  1. Code reusability: Generic lambdas allow you to write code that can operate on multiple types without the need for explicit template parameters. This promotes code reusability and reduces code duplication.

  2. Improved readability: Generic lambdas make the code more readable by eliminating the need for explicit type declarations. The use of the auto keyword allows the compiler to deduce the types of the lambda arguments, making the code more concise and easier to understand.

  3. Flexibility: Generic lambdas provide flexibility by allowing you to write code that can handle different types of arguments. This can be useful in scenarios where the exact type of the arguments is not known in advance.

Overall, generic lambdas are a powerful feature in C++ that enable you to write more generic and flexible code.

5. Can lambda functions be stored and passed as arguments in C++?

Yes. Lambda functions can be stored in variables and passed as arguments, just like any other callable. The idiomatic way depends on the context.

Storing a lambda in auto:

auto square = [](int x) { return x * x; };
std::cout << square(5);  // 25

Storing in std::function — use this when you need to store a lambda that captures variables, store it in a container, or erase its exact type:

#include 

std::function transform = [](int x) { return x * 2; };
transform(7);  // 14

// Stored in a container
std::vector> callbacks;
callbacks.push_back([]{ std::cout << "hello\n"; });
callbacks.push_back([]{ std::cout << "world\n"; });
for (auto& cb : callbacks) cb();

Passing a lambda to a function — prefer a template parameter for maximum performance (avoids std::function overhead):

template 
void apply(const std::vector& v, Func f) {
    for (int x : v) f(x);
}

apply(v, [](int x) { std::cout << x << '\n'; });

C++20: using Concepts to constrain callable parameters:

template  Func>
void apply(const std::vector& v, Func f) {
    for (int x : v) f(x);
}

Trade-offs between auto and std::function:

auto / template std::function
Performance Zero overhead, inlined Heap allocation, virtual dispatch
Flexibility Monomorphic (one type) Type-erased, storable anywhere
Stateful lambdas Yes Yes
Returning from function Needs auto return type Easy — concrete type

C++23 std::move_only_function is a lighter alternative to std::function that supports move-only callables (e.g., lambdas that capture unique_ptr) and avoids unnecessary copies.

↑ Back to top

Follow-up 1

How can you store a lambda function in a variable?

To store a lambda function in a variable, you can use the auto keyword to automatically deduce the lambda's type. Here is an example:

auto myLambda = [](int x) { return x * 2; };

Follow-up 2

Can you provide an example of passing a lambda function as an argument to another function?

Yes, here is an example of passing a lambda function as an argument to another function:

#include 
#include 

void performOperation(int x, std::function operation) {
    int result = operation(x);
    std::cout << "Result: " << result << std::endl;
}

int main() {
    int value = 5;
    performOperation(value, [](int x) { return x * 2; });
    return 0;
}

In this example, the performOperation function takes an integer x and a lambda function operation as arguments. The lambda function is then called with the value of x and the result is printed.

Live mock interview

Mock interview: Lambda Functions in C++

Intermediate ~5 min Your own free AI key

Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.