Mastering Lambda Functions in C++: A Complete Guide with Practical Examples

Madhawa Polkotuwa
5 min readOct 29, 2024

--

C++ Tips

Introduction:
Lambda functions in C++ provide a concise and flexible way to create inline, anonymous functions. Introduced in C++11, and enhanced in later versions, Lambdas are useful in situations where you need short snippets of code without the overhead of a full function definition.

In this post, we’ll cover the basics of Lambda functions, how to pass parameters, capture external variables, and even control return types. Whether you’re new to Lambdas or just looking for a refresher, this guide will give you the skills to start using them confidently in your C++ projects.

What Are Lambda Functions?

A Lambda function in C++ is an unnamed function that can be defined within another function or scope. It follows this general syntax:

[capture](parameters) -> return_type {
// function body
};
  • Capture: Determines which external variables (in the scope where the Lambda is defined) the Lambda can access.
  • Parameters: Defines inputs for the Lambda, similar to regular function parameters.
  • Return Type: Specifies the type of value the Lambda will return (optional, as the compiler can infer it in many cases).

Getting Started: Basic Examples

Let’s look at some straightforward examples to see how Lambdas work.

Example 1: Simple Lambda Function

A basic Lambda with no parameters or return type:

#include <iostream>

auto Hello = []() {
std::cout << "Hello, World!" << std::endl;
};

int main() {
Hello(); // Output: Hello, World!
return 0;
}

In this example, Hello is a Lambda function that prints "Hello, World!". Since the function has no parameters or return type, we omit them for simplicity.

Example 2: Lambda with a Return Value

A Lambda can return values, just like regular functions:

auto HelloString = []() {
return "Hello, Lambda!";
};

int main() {
std::cout << HelloString() << std::endl; // Output: Hello, Lambda!
return 0;
}

Here, HelloString returns a string. We call it and print the result to see the output.

Using Parameters and Return Types in Lambdas

Lambda functions can take parameters, just like any other function. Let’s go through some examples with parameters and return types.

Example 3: Lambda with Parameters

This example shows a Lambda that takes two parameters and returns their sum:

auto Add = [](double a, double b) {
return a + b;
};

int main() {
std::cout << "Sum: " << Add(3.2, 2.1) << std::endl; // Output: Sum: 5.3
return 0;
}

Here, Add takes two double parameters and returns their sum. The simplicity of the syntax makes Lambdas great for quick calculations.

Example 4: Explicitly Specifying the Return Type

Sometimes, you may want to control the return type explicitly, especially if the return type would be ambiguous or inferred incorrectly. You can specify the return type with -> syntax.

auto AddInt = [](double a, double b) -> int {
return a + b;
};

int main() {
std::cout << "Sum (as int): " << AddInt(3.2, 2.1) << std::endl;
// Output: Sum (as int): 5
return 0;
}

In this case, AddInt explicitly returns an int by truncating the sum of two double values. Specifying the return type as int changes the result from 5.3 to 5.

Capturing External Variables

One of the most powerful features of Lambdas is capturing external variables. There are two main capture modes:

  1. Capture by Value: Captures a copy of the variable’s value at the time the Lambda is defined.
  2. Capture by Reference: Captures a reference to the variable, meaning changes to the variable outside the Lambda will affect it inside, and vice versa.

Example 5: Capture by Value

Let’s look at a Lambda that captures a variable by value.

int a = 10;

auto showValue = [a]() {
std::cout << "Captured by value: " << a << std::endl;
};

int main() {
showValue(); // Output: Captured by value: 10
a = 20;
showValue(); // Output: Captured by value: 10 (still the same)
return 0;
}

Here, a is captured by value, so even if a changes outside the Lambda, the value inside the Lambda remains the same.

Example 6: Capture by Reference

When you need the Lambda to reflect changes in the variable outside its scope, capture it by reference using the & symbol.

int b = 10;

auto showReference = [&b]() {
std::cout << "Captured by reference: " << b << std::endl;
};

int main() {
showReference(); // Output: Captured by reference: 10
b = 20;
showReference(); // Output: Captured by reference: 20
return 0;
}

In this case, b is captured by reference, so changes to b outside the Lambda reflect inside the Lambda.

Advanced Capturing: Mixed Mode

Sometimes, you may want to capture some variables by value and others by reference.

int x = 5;
int y = 10;

auto mixedCapture = [x, &y]() {
std::cout << "x (by value): " << x << ", y (by reference): " << y << std::endl;
};

int main() {
mixedCapture(); // Output: x (by value): 5, y (by reference): 10
y = 20;
mixedCapture(); // Output: x (by value): 5, y (by reference): 20
return 0;
}

Here, x is captured by value, so it remains 5 inside the Lambda, whereas y is captured by reference, allowing changes to y outside the Lambda to reflect within it.

Capturing All Variables

For quick access to all variables in the surrounding scope, you can use = or & in the capture clause to capture all by value or reference, respectively.

  • Capture All by Value: [=]() { /* code */ }
  • Capture All by Reference: [&]() { /* code */ }
int a = 5, b = 10;

auto captureAllByValue = [=]() {
std::cout << "a: " << a << ", b: " << b << std::endl;
};

auto captureAllByReference = [&]() {
std::cout << "a: " << a << ", b: " << b << std::endl;
};

int main() {

captureAllByValue(); //
a = 15;
b = 20;
captureAllByReference(); // Reflects new values of a = 15 and b = 20
return 0;
}

The first Lambda captures all variables by value, freezing their values inside the Lambda, while the second captures all by reference, making it sensitive to changes.

YouTube Video

Conclusion

Lambda functions in C++ are a powerful way to make your code cleaner and more concise, especially for small, local operations. They’re commonly used in functional programming, sorting, event handling, and other areas where small, flexible functions are needed.

Understanding how to use Lambda functions with different capturing modes and return types will give you new options for writing clear, efficient code. Try incorporating Lambdas into your own C++ projects and see the difference they can make!

--

--

No responses yet