Chapter 22 - Notes

Lambda Expressions

Lambda expressions are a compact way to define function objects without a name - which is why it is also sometimes referred to as anonymous functions (anonymous to mean without a name).

A lambda expression for simply printing an integer is as follows:

[] (const int& number) { std::cout << number << std::endl; }

And its general form:

[<capture group>] (<parameter list>) { <statement> }; 

Capture Groups

The capture group component, [], can be used to give a lambda expression state by capturing one or more variables in the current scope.

int divisor = 2; // initial value

// ...

auto element = find_if (
    begin of a range,
    end of a range,
    [divisor](int dividend){ return (dividend % divisor) == 0; } 
);

By default captured variables are constant and copied by value.

Modify captured variables

To allow modifications to the captured variables, use the mutable keyword:

[stateVar1, stateVar2](Type& param) mutable { ... }

Note that the capture variables are still copied by value, and so changes made to it within the lambda expression will not take effect outside of it.

To ensure modifications made to captured variables are also persisted outside of it, the variables need to be captured by reference:

[&stateVar1, &stateVar2](Type& param){ ... }

Capture all variables

To capture all variables in the local scope, use the following syntax:

 [=]( ... ){ ... }

Parameter List

The parameter list component, (), can be used to specify zero or more parameters accepted by the lambda expression. The syntax is the same as a parameter list for a plain old function, each parameter separated by commas, and rules around const and references all apply the same.

Statement

The compound statement component, {}, can hold one or more statements, each separated by a semicolon (;):

[stateVar1, stateVar2](Type1 var1, Type2 var2) {
    return (value or expression);
}

Explicit Return Types

Note that the return type for lambda expressions need not be specified, the compiler will deduce this for you. However if you want to explicitly specify this to not leave disambiguation to the compiler, you can do so in the following:

[...](...) -> RETURN_TYPE { ... }

If your lambda expression spans multiple lines, an explicit return type is required.

Full Syntax

An example using the full syntax of a lambda expression:

[&stateVariable1, stateVariable2](const Type1& param1, Type2 param2) mutable -> ReturnType {
    Statement_1;
    Statement_2;
    return (Expression or Value);
}

Capture group:

  • stateVariable1 is a variable captured by reference; modifications to this value within the lambda expression will persist outside of it as well.

  • stateVariable2 is a variable captured by value; modifications to this value will not persist outside of it.

Parameter list:

  • param1 is a constant reference parameter of type Type1 which can be used within the lambda expression

  • param2 is a value parameter of type Type2 which can be used within the lambda expression

Keywords:

  • The mutable keyword allows modifications to the stateVariable1 and stateVariable2 variables.

  • The lambda expression's return type is ReturnType

Statements:

  • The lambda expression has 3 statements, the last being a return statement returning either an expression or a value.

Function Objects vs Lambdas

Lambda Expressions

Function Objects

  • Use when the of the function is simple, and is only used in one part of your application

  • Simple lambda functions can often be inlined by the compiler (moreso than plain functions!)

  • Use when the logic of the function and maintenance of state is complex

  • Use when the function might be re-used in other parts of your application

Last updated

Was this helpful?