C++11 Lambda: Capture Variables by Reference or Value

Lambda Functions were introduced in C++11, and it allows us to write Inline, Anonymous Functions. Lambda Functions are also called Lambda Expressions. These Lambda expressions/functions are particularly useful when you need to pass a small function as an argument to another function as callbacks, such as a sorting algorithm or a thread constructor. In this article, we’ll look how to capture local variables within a Lambda Expression / lambda Function in C++.

What Are Lambda Expressions in C++?

Before we explore variable capturing, let’s understand the basic syntax of a lambda expression:

[Captured variables](parameters) { function code }

The [] is known as the capture clause, the () holds any parameters the lambda accepts (like a regular function), and the {} contains the function code that defines the body of the lambda expression.

Capturing Local Variables into a Lambda Function in C++

When you define a lambda expression within a function, you may want to use variables from its enclosing scope, also known as the outer scope. To do this, you can capture these variables in two main ways:

  1. By Value
  2. By Reference

Capturing Variables by Value into a Lambda Function in C++

Capturing by value means the lambda will have its own copy of the captured variables. These copies are made at the point where the lambda is defined, not when it’s called. Here’s how you can capture variables by value:

// Local Variables
std::string msg = "Hello";
int counter = 10;

// Defining Lambda function and Capturing Local variables by Value
auto func = [msg, counter]() {
    // Accessing the captured variables
};

The variables msg and counter are now available within the lambda, but they’re const by default – meaning you cannot modify them. To do so, you need to declare the lambda as mutable.

auto func = [msg, counter]() mutable {
    // Now we can modify msg and counter
};

Here is a complete example,

#include <iostream>
#include <string>

int main() {
    std::string msg = "Hello";
    int counter = 10;

    auto func = [msg, counter]() mutable {
        std::cout << "Inside Lambda :: msg = " << msg << std::endl;
        std::cout << "Inside Lambda :: counter = " << counter << std::endl;
        msg = "Temp";
        counter = 20;
        std::cout << "Inside Lambda :: After changing :: msg = " << msg << std::endl;
        std::cout << "Inside Lambda :: After changing :: counter = " << counter << std::endl;
    };

    func();

    std::cout << "msg = " << msg << std::endl;
    std::cout << "counter = " << counter << std::endl;

    return 0;
}

Output

Inside Lambda :: msg = Hello
Inside Lambda :: counter = 10
Inside Lambda :: After changing :: msg = Temp
Inside Lambda :: After changing :: counter = 20
msg = Hello
counter = 10

As you can see, changes made to msg and counter within the lambda do not affect the originals outside it.

Capturing Variables by Reference into a Lambda Function in C++

If you want your lambda to reflect changes to the captured variables back to the outer scope, you should capture them by reference:

// Local Variables
std::string msg = "Hello";
int counter = 10;

// Defining Lambda function and Capturing Local variables by Reference
auto func = [&msg, &counter]() {
    // Inside lambda, msg and counter refer to the original variables
};

A complete example of capturing by reference would look like this:

#include <iostream>
#include <string>

int main() {
    std::string msg = "Hello";
    int counter = 10;

    auto func = [&msg, &counter]() {
        std::cout << "Inside Lambda :: msg = " << msg << std::endl;
        std::cout << "Inside Lambda :: counter = " << counter << std::endl;
        msg = "Temp";
        counter = 20;
        std::cout << "Inside Lambda :: After changing :: msg = " << msg << std::endl;
        std::cout << "Inside Lambda :: After changing :: counter = " << counter << std::endl;
    };

    func();

    std::cout << "msg = " << msg << std::endl;
    std::cout << "counter = " << counter << std::endl;

    return 0;
}

Output

Inside Lambda :: msg = Hello
Inside Lambda :: counter = 10
Inside Lambda :: After changing :: msg = Temp
Inside Lambda :: After changing :: counter = 20
msg = Temp
counter = 20

Notice how the changes within the lambda are reflected outside

it because msg and counter are captured by reference.

Capturing All Local Variables

You can also capture all local variables without specifying them one by one:

  • By value using [=]
  • By reference using [&]

And you can mix modes:

auto func = [=, &counter]() mutable {
    // All variables captured by value, except counter by reference
};

Capture Variables Carefully in Lambda Function in C++

Be cautious when capturing by reference, especially if the lambda might outlive the captured variables. If a lambda captures a reference to a local variable that goes out of scope, accessing that variable later will lead to undefined behavior, potentially crashing your program.

Here’s an example that demonstrates the danger:

#include <iostream>
#include <functional>

std::function<void()> getCallback() {
    int counter = 10;
    auto func = [&counter]() mutable {
        std::cout << "Inside Lambda :: counter = " << counter << std::endl;
        counter = 20;
    };
    return func;
}

int main() {
    std::function<void()> func = getCallback();
    func();  // Undefined behavior: the original counter is gone
    return 0;
}

Output

Inside Lambda :: counter = 22092
Segmentation fault (core dumped)

Here, we tried to access a variable that had already been destroyed due to stack unwinding. Basically, the lambda Expression in this example outlives counter, leading to a crash when it tries to modify it.

Compiling Lambda Expressions in C++

To compile code with lambda expressions in C++, make sure to enable the C++11 (or later) standard:

g++ -std=c++11 example.cpp

Summary

Lambda expressions offer a concise and powerful way to define local functions that can access and manipulate variables in their enclosing scope. By understanding how to capture variables by value or by reference, you can write more expressive and functional code in C++. Always remember to manage the scope of your variables carefully to avoid undefined behavior.

4 thoughts on “C++11 Lambda: Capture Variables by Reference or Value”

  1. Inside Lambda :: counter = -858993460
    Inside Lambda :: After changing :: counter = 20
    Press any key to continue . . .

    It is not crashing, please find the output

    1. Hi Atul,

      Well its an undefined behaviour, some times it will crash, sometimes will give garbage values.

      See the value of counter on your output, its garbage.

      I tested on linux and it crashed. So, its undefined behaviour.

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top