C++ 17 – lambda expressions are powerful

lambda expressions are introduced in C++ 11 and have provided many features for the developer. With C++ 17, these got further improved. The improved features in C++ 17 for lambda expressions are as below.

lambda expressions are constexpr by default

If the lambda expressions are such that it is possible at compile-time itself to evaluate them, as lambda expressions are constexpr by default in C++ 17, they will be a massive performance boost.

Here, square(5) will get evaluated at compile time itself, and the code will become “result = 25” by end of the compilation.

constexpr auto square = [](int val) { return val * val; };
constexpr int result = square(5);

lambda allows auto in arguments

Here, a lambda expression can take both int and float values for x and y, as C++ 17 allows auto in arguments. This makes the lambda expression polymorphic.

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

lambda can capture ‘this’ pointer

A lambda can capture this pointer for the current object. It is helpful in cases where the object may get changed on the fly. In those cases, we can make a local copy of the object and do further processing. It is helpful in asynchronous calls, call-back functions, and other scenarios.

Here is an example where a sum computation task is a lambda. As soon as we call the lambda, it captures this pointer as an argument, and we can use it inside the function. This program is just for demonstration, but we can copy the object and further process it as required.

The program is as below. async_increment is the lambda that captures this. It can have just this also as the argument. The program is available for download at https://github.com/codeversionmaster/cplusplus/blob/cplusplus17/lambda_this.cpp.

$ cat lambda_this.cpp 
#include <iostream>
#include <future>
#include <thread>
#include <chrono>

class Increment {
    private:
        int count;

    public:
        Increment(int start_count) : count(start_count) {}

        void increment(int value) {
            count += value;
        }

        // Here this would store the this pointer of object of current context
        void async_increment(int value) {
            auto task = [this, value] {
                this->increment(value);
            };
       
            // Launch the async task 
            std::future<void> result = std::async(std::launch::async, std::move(task));
            // Wait for the async task to complete
            result.wait();
            std::cout << "Incremented count: " << count << '\n';
    }
};

int main() {
    Increment counter(0);
    counter.async_increment(5);
    return 0;
}

std::async spawns an asynchronous task defined by lambda task. result.wait would wait for the result, and then we print the incremented count.

We can compile and run the program as below.

$ g++ -std=c++17 lambda_this.cpp -o lambda_this
$ ./lambda_this 
Incremented count: 5