C++ Lambdas Primer (1)

Lambdas have been an important part of Modern C++ for over a decade, having been added to the language with the release of C++11. Whilst very useful in their own right, proficient use of lambdas touches upon many different areas of C++: functions, reference semantics, generics, operator overloading, RVO… the list goes on. It has been said that having a complete knowledge of C++ lambdas leads to an understanding of the language as a whole; this mini-series aims to uncover some of their secrets and hopefully serves as a practical introduction.

Lambda functions (commonly abbreviated to just “lambdas”) are functions treated as objects, meaning they can be (re-)assigned to variables and returned by (or passed to) other functions. Really, the concept is no more complex than that!

Diving straight in, the following code example should be obvious, note that the lambda function is called g, nested inside f():

#include <iostream>

void f() {
    auto g = []{
        std::cout << "Hello, lambdas!\n";
    };
    g();
}

int main() {
    f();
}

Note that g() must be explicitly called by f() (after being defined within the same function) in order for there to be any output, in the same way that f() must be called from main(). The lambda object (variable) g is local to f() and a semi-colon is required after the body of the lambda in order to terminate the assignment statement.

Now consider a variation on this theme, where f() returns the lambda (f()‘s return type is now auto, another addition to C++11):

#include <iostream>

auto f() {
    auto g = []{
        std::cout << "Hello, lambdas!\n";
    };
    return g;
}

int main() {
    f()();
}

Note the double parentheses in main()‘s call of f(), this is correct as we are explicitly calling (or invoking) the function returned by the call to f().

Now let’s pass a lambda to another function:

#include <iostream>
#include <functional>

void f(std::function<void(void)> g) {
    g();
}

int main() {
    f([]{ std::cout << "Hello, lambdas!\n"; });
}

Starting with the call from main(), we call f() with a single parameter, this being an unnamed temporary. The minimal syntax for a lambda is an opening and closing square bracket followed by the (arbitrarily complex) body of the function enclosed in curly braces. To specify the type of the lambda parameter to f() itself (having no parameters and void return type), we use std::function as we can’t use auto here.

With the ways lambdas can be defined and passed around demonstrated, let’s take a look at more complex lambdas which can accept and return values (whilst being pure functions, that is not modifying global or external state). The following code example defines two lambdas called increment and decrement, both of which accept and return an int. The lambda syntax is the same as before but now with a standard parameter list (enclosed in parentheses) between the closing square bracket and the opening curly brace:

#include <iostream>
#include <functional>

void f(std::function<int(int)> g, int i) {
    std::cout << "f(): " << g(i) << '\n';
}

int main() {
    auto increment = [](int a){ return a + 1; };
    auto decrement = [](int a){ return a - 1; };

    f(increment, 10);
    f(decrement, 0);
}

Looking again at f() first, note that it accepts two parameters, the first being a function g as before (strictly speaking, this need not necessarily be a lambda function, a global one is allowed too) and the second being a value i. It then simply outputs the result of calling g with parameter i. Within main(), two lambdas are defined before passing each of them to f() with a second (non-lambda) parameter. Thus the behaviour of f() is modified according to both the choice of lambda function and the value of the parameter. The output of this program is:

f(): 11
f(): -1

We’ve seen how C++ lambdas can be defined, passed around and invoked. Next time we’ll look at generic lambdas, which can take and return parameters of types not explicitly specified.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s