1.1: Lambda expressions
C++ supports  lambda expressions. As we'll see in
chapter ?? generic algorithms often accept arguments that can
either be function objects or plain functions. Examples are the sort
(cf. section ??) and find_if (cf. section ??) generic
algorithms. As a rule of thumb: when a called function must remember its
state a function object is appropriate, otherwise a plain function can be
used.

JB: drop: Frequently the function or function object is not readily available, and it
JB: drop: ...

JB: propose: Frequently the function or function object is not yet available. Lambda functions
JB: propose: provide a way to define a simple function object on the spot. Like ordinary functions
JB: propose: they should be short so as not to drown the reader in details.

> Ik laat de tekst zoals die is. Jouw alternatief is korter, maar geeft geen
> achtergrondinformatie waarom/wanneer die lambda functies handig zijn, en is
> voornamelijk nuttig wanneer 't concept 'lambda functie' al bekend is (zoals
> bij jou ;-)


1.1.1: Lambda expressions: syntax
    A lambda expression defines an
    anonymous function object, also called a
    closure object or simply a closure. 

When a lambda expression is evaluated it results in a temporary function
object (the closure object). This temporary function object is of a unique
anonymous class type, called its closure type.

Lambda expressions are used inside blocks, classes or namespaces (i.e., pretty
much anywhere you like).

JB: drop: Their implied closure type is defined in the smallest
JB: drop: block, class or namespace scope containing the lambda expression.

JB: propose: Their implied closure type is defined in the smallest enclosing
scope.

> OK, ik voeg de voorbeelden in een 'e.g.,' toe.


captureThe closure
object's visibility starts at its point of definition and ends where its
closure type ends (their visibility is identical to the visibility of plain
variables).

The closure type defines a  public inline const function call
operator. Here is an example of a lambda expression:
    
    
        
    []                      // the `lambda-introducer' JB: 'capture clause'
    (int x, int y)          // the `lambda-declarator' JB: 'parameter list'

> 'capture cause' wordt niet in de N4917 versie van de standaard
> gebruikt. Lambda introducer en lambda-declarator wel (7.5.5.1): ik daarom
> daarom lambda-introducer en lambda-declarator staan.


    {                       // the function's body
        return x * y;
    }


This function (formally: the function call operator of the closure type
created by this lambda expression) expects two int arguments and returns
their product. It is an inline const member of its closure type. The
function's const attribute is removed if the lambda expression specifies
 mutable. E.g.,
        
    [](int x, int y) mutable
    ...


JB: Sinds c++23 kan de function call operator behalve mutable ook static zijn.

> voeg ik toe.


JB: drop: The lambda-declarator may be omitted if no parameters are defined, but when
JB: drop: specifying mutable (or constexpr, see below) a lambda-declarator must
JB: drop: be specified (at least by specifying an empty set of parentheses).

JB: propose: The lambda-declarator may be omitted if no parameters are defined, unless
JB: propose: the 'mutable' keyword is used.

> zelfs dat is niet nodig: in een class X::fun kan dit bv worden geschreven:

>   void X::fun()
>   {
>       int loc = 1;
>
>       [loc] mutable       // geen ()
>       {
>           ++loc;
>       }();
>   }


JB: false: The parameters in a lambda declarator cannot be given default arguments.

> inderdaad: wordt aangepast.



Lambda declarator specifiers can be specified as mutable, constexpr,
or both. When specifying constexpr the lambda-expression is a
constexpr, which may be compile-time evaluated if its arguments qualify as
const-expressions. If a lambda-expression is defined inside a constexpr
function then the lambda-expression itself automatically becomes a
constexpr itself, and the constexpr lambda declarator specifier is not
required. Thus, the following two function definitions are identical:

JB: Ik zet 'constexpr' liever vòòr het return-type, omdat het geen eigenschap
JB: van het type is, maar van de hele functie.

> OK. 'constexpr' wordt in de standaard ook vooraan geplaatst. Pas ik aan in
> de Annotaties, ook elders.


    int constexpr change10(int n)
    {
        return [n] 
               { 
                   return n > 10 ? n - 10 : n + 10; 
               }();
    }
    
    int constexpr change10(int n)
    {
        return [n] () constexpr 
               { 
                   return n > 10 ? n - 10 : n + 10; 
               }();
    }


The lambda expression defined at the top of this section could for example be
used in combination with the accumulate generic algorithm (cf. section
??) to compute the product of a series of int values stored in a
vector:
        
    cout << accumulate(vi.begin(), vi.end(), 1,
                [] (int x, int y) 
                { 
                    return x * y; 
                }
            );


This lambda expression implicitly defines its return
        
    type as decltype(x * y). Implicit return types can be used in these
cases:
    
     the lambda expression does not contain a return statement (i.e.,
        it's a void lambda expression);
     the lambda expression contains a single return statement; or
     the lambda expression contains multiple return statements
        returning values of identical types (e.g., all int values).
    

If there are multiple return statements returning values of different
types then the lambda expression's return type must explicitly be specified
using a late-specified return type,
(cf. section ??):
        
    [](bool neg, double y) -> int
    {
        return neg ? -y : y;
    }
    JB: Nee. Als het late return type wordt weggelaten is het return type een
    JB: double, waarnaar een bool geconverteerd kan. Good practice is dat niet...

> klopt, maar weggelaten return types worden al genoemd. Dit gaat over
> situaties waarbij de return types niet automatisch correct worden bepaald.


//JB: drop: Variables which are visible at the location of a
//JB: drop: lambda expression may be accessed by the lambda expression's compound
//JB: drop: statement. Which variables and how they are accessed depends on specifications
//JB: drop: in the lambda-introducer.

//JB: propose: Variables visible at the location of a lambda expression may be accessed by the
//JB: propose: lambda expression's function body, provided that they are captured.
//JB: propose: Which variables are captured how depends on the capture clause.

> nee: lambda expressions hebben wel een compound statement maar geen functie
> body. Bv, ze kunnen geen functie try blocks definieren. Vandaar compound
> statement. 



In the following overview: when it's stated that
`variables can be modified' then that only applies to variables that
themselves allow modifications.
 
     Global variables are always accessible, and can be modified if their
        definitions allow modifications;
JB: drop:      If the lambda expression is defined inside a function then that
JB: drop:         function's local variables may be accessed by the lambda expression's
JB: drop:         compound statement if their names are listed inside the
JB: drop:         lambda-introducer.<br/>
JB: drop:        The specification local is used below as a shorthand for a
JB: drop:         (comma-separated) list of names of local variables of the surrounding
JB: drop:         function that are visible in the lambda expression's compound
JB: drop:         statement;
JB: drop:      Local variables specified in the lambda introducer are in fact data
JB: drop:         members of the lambda expression which are initialized by the
JB: drop:         corresponding local variables of the function surrounding the lambda
JB: drop:         expression. This is illustrated by the following example showing
JB: drop:         different addresses for var (however, copying can be prevented as
JB: drop:         covered below):
JB:propose:      Names in the capture clause define data members of the lambda object.
JB:propose:         They are initialized using identically-named variables in the enclosing
JB:propose:     scope, if those exist.
JB:propose:      Default member initializers can be specified.

> ik handhaaf de huidige versie: jouw kortere versie is prima voor een ervaren
> lambda-expressie gebruiker, maar te weinig gedetailleerd voor een 1e contact.


    #include <iostream>

/JB: drop:     int main()
/JB: drop:     {
/JB: drop:         int var = 12;
/JB: drop:         std::cout << &var << '\n';
/JB: drop:         [var]               // lambda expr. using 'var'
/JB: drop:         {
/JB: drop:             std::cout << &var << '\n';
/JB: drop:         }();                // directly call the lambda expr.
/JB: drop:     }

//JB: propose:     int main()
//JB: propose:     {
//JB: propose:         struct Date
//JB: propose:         {
//JB: propose:             int month;
//JB: propose:             int day;
//JB: propose:         };
//JB: propose:     
//JB: propose:         int age = 50;
//JB: propose:         int length = 1960;
//JB: propose:         Date birthday{3, 13};
//JB: propose:         
//JB: propose:         std::cout <<
//JB: propose:             [
//JB: propose:             age = age + 17, // 'age' is a data member of type int,
//JB: propose:                                  // initialized to 67
//JB: propose:             &length,             // 'length' is a data member of type int &,
//JB: propose:                                  // and refers to the enclosing 'length'
//JB: propose:             // birthday.month,   // WC: cannot have a data member '.month'.
//JB: propose:             text = "unidentified"
//JB: propose:             ]
//JB: propose:         {
//JB: propose:             return age >= 18 && length > 1400 ? "maybe adult human" : text;
//JB: propose:         }() << '\n';
//JB: propose:     
//JB: propose:     }

> ik laat 't voorbeeld ongewijzigd: de sectie gaat over lokale
> functievariabelen. De toevoeging van een extra struct en een hele serie
> extra variabelen maakt 't voorbeeld onnodig complex.


     When the lambda expression is defined inside a class member function
        the lambda-introducer may specify this or *this allowing the
        lambda function to access/use the class's members (not defining copies
        as when accessing local variables). When this is specified the
        class's members can be accessed and the class's (non-const) data
        members can be modified. When *this is specified the lambda
        expression uses a copy of the class's object. 

JB: Not true. The capture clause: `[*this]` only works if the copy constructor is
JB: available, and will copy. (Perhaps copy elision is applied. Didn't try.)

> Da's correct maar triviaal. Uiteraard kun je geen kopie maken als er geen
> copy constructor beschikbaar is.


By default lambda expressions define constant (immutable) function objects,
but by appending mutable to the lambda declarator they are defined as
mutable function objects.  Sometimes using mutable or not is
irrelevant. In those cases `mutable_opt' is specified below. If
mutable_opt is not specified then specifying mutable indicates that
the lambda expression's function isn't a const member function anymore, so
it may modify its (modifiable) data members.

//JB: drop: Below specifications (...) refer to the lambda expression's declarator,
//JB: drop: and lambda introducers ending in  , ...] indicate that they may
//JB: drop: specify , this or , *this.<br/>
//JB: drop:     There is no required ordering of the this, *this and local
//JB: drop: specifications. 
//JB: drop: 
//JB: drop: 
//JB: drop:     [...] (...) mutable_opt:
//JB: drop:        only global variables can be accessed;
//JB: drop: 
//JB: drop: [local, ...] (...):
//JB: drop:        like the previous specification, with local being defined as the
//JB: drop:         lambda expression's data member(s) initialized by copying
//JB: drop:         the surrounding function's local variable(s).  When mutable is
//JB: drop:         specified the (copied) local variable(s) can be modified;
//JB: drop: 
//JB: drop: [&local, ...] (...):
//JB: drop:        like the previous specification, but the local variable(s) specified
//JB: drop:         using &-prefixes are available as references to the class's data
//JB: drop:         members. They can be modified if those members are non-const;
//JB: drop: 
//JB: drop: [&d_local = std::as_const(local), ...] (...) 
//JB: drop:        defines the lambda expression's d_local data member as a reference
//JB: drop:         to the (immutable) local variable of the surrounding function. The
//JB: drop:         d_local data members remains a reference to an immutable local
//JB: drop:         variable whether or not mutable is specified.<br/>
//JB: drop:        To use std::as_const the <utility> header file must be
//JB: drop:         included;
//JB: drop: 
//JB: drop: [&, ...] (...)  mutable_opt:
//JB: drop:        the `local reference specifier': local variables are visible as
//JB: drop:         modifiable references. If some of those references should not be able
//JB: drop:         to modify their local variables then use specifications like
//JB: drop:         &d_local = std::as_const(local) for those variables.
//JB: drop: 
//JB: drop: [vars, ...] (...): 
//JB: drop:        in addition to the above specifications lambda expressions may define
//JB: drop:         their own data members. E.g., to define a lambda expression having its
//JB: drop:         own int count and double value data members, also allowing it
//JB: drop:         to access the caller's local variables its lambda-introducers can be
//JB: drop:         specified as [&, count = int(0), value = double(0)].<br/>
//JB: drop:        Such local variables are defined by specifying their names, followed by
//JB: drop:         their type-specifying initializations;
//JB: drop: 
//JB: drop: [=, ...] (...): 
//JB: drop:        if the lambda introducer starts with = then all local variables are
//JB: drop:         available as copies of the surrounding function's local variables;
//JB: drop: 
//JB: drop: [=, &local, ...]: 
//JB: drop:        like the previous specification, but the local variable(s) prefixed by
//JB: drop:         & are available as references. If those local variables are
//JB: drop:         non-const they can be modified. If some of those references should not
//JB: drop:         be able to modify their local variables then use specifications like
//JB: drop:         &d_local = std::as_const(local);

//JB: propose: The first `parameter` of a lambda expression's capture clause can
//JB: propose: be a nameless '&' or '=', with special meaning:
//JB: propose: 
//JB: propose:     [=]            Capture all that is in scope by value.
//JB: propose:     [&]            Capture all that is in scope by reference.
//JB: propose:     [=, ...],      Capture all that is in scope by value, but special-case ...
//JB: propose:     [&, ...],      Capture all that is in scope by reference, but special-case ...
//JB: propose:     [=, &age]      Only age is captured by reference, the rest of what is in scope by value.
//JB: propose:     [&, age]       Only age is captured by value, the rest of what is in scope by reference.
//JB: propose: Note that the special-cased variables are just data members as
//JB: popose:  specified had there been no '=' or '&' default-capture-mode
> specifier.

> er zijn veel manieren om de lambda introducer te specificeren. In jouw
> overzicht ontbreekt (of ik kijk eroverheen) de vermelding van puur globale
> variabelen, dan wel variaties waarbij de & of = prefixes niet worden
> gebruikt ontbreken. 

//JB: I don't think so?!: Even when not specified, lambda expressions defined inside class member
//JB: I don't think so?!: functions implicitly capture
//JB: I don't think so?!:      their this
//JB: I don't think so?!: pointers, and class members are always accessed relative to this. 

> Ja, dat klopt en dat wordt ook vermeld:
>   Below specifications (...) refer to the lambda expression's declarator,
>   and lambda introducers ending in , ...] indicate that they may
>   specify , this or , *this
 
When members are called asynchronously (cf. chapter ??) a
problem may arise, because the asynchronously called lambda function may refer
to members of an object whose lifetime ended shortly after asynchronously
calling the lambda function. This potential problem is solved using
`*this' in the lambda-introducer if it starts with = (e.g., [=,
*this]). In this case when the object's scope ends it is not immediately
destroyed, but its lifetime is extended for the duration of the
lambda-expression's execution. Consider the following example:

JB: I don't understand.
JB: g() WC, because `this` is not in scope of its lambda functions' bodies.

> Ik snap 't ook niet ;-) Waarschijnlijk een overblijfsel van lang geleden. Ik
> haal 't voorbeeld weg.


Although lambda expressions are anonymous function objects, they can be
assigned to variables. Often, the variable is defined using the keyword
auto. E.g.,
        
    auto sqr = [](int value)
               {
                   return value * value;
               };


The lifetime of such a lambda expression is equal to the lifetime of sqr,
which is the variable receiving the lambda expression as its value.

Defining a lambda expression is different from calling its function
operator. The function S2::f() returns what the lamba expression (1)'s
function call operator returns: its function call operator is called by using
() (at (3)). What it in fact returns is another anonymous function object
(defined at (2)). As that's just a function object, to retrieve its value
it must still be called from f's return value using something like this:
        
    S2 s2;      //JB: propose: // Define the object.
    s2.f()();   //JB: propose: // Call the function call operator.


//JB: drop: Here, the second set of parentheses activates the returned function
//JB: drop: object's function call operator. Had the parentheses been omitted at (3) then
//JB: drop: S2::f() would have returned a mere anonymous function object (defined at
//JB: drop: (1)), in which case it would require three sets of parentheses to retrieve
//JB: drop: ohseven's value: s2.f()()().

> commentaar is toegevoegd, de opmerking blijft staan om het dubbel gebruik
> van () te benadrukken.


1.1.2: Using lambda expressions
//JB: typo: "has" been covered. 
> correct.
        Now that the syntax of lambda expressions have been covered let's see


they can be used in various situations.

<<<JB: tot hier so far>>>
