banner
Matrix

Matrix

Abyss
email
github

Petty C++ - lambda

Lambda expressions are a concise syntax for defining anonymous functions in versions of C++ after C++11. In some contexts, such as STL algorithms and asynchronous operations, using lambda as a callback function is natural and convenient, and lambda is very compatible with C++'s functional programming features.

Basic Syntax#

A complete lambda expression syntax is as follows:

[captureList](paramList) mutable throw() -> returnType {
  // do something..
}

Capture List#

A lambda starts with a square bracket capture list, which is used to define which external variables can be used by the lambda expression and how to use them (by value or by reference).

The capture list can be empty, indicating that the lambda does not use any variables outside the closure, but the square brackets cannot be omitted.

Both by-value and by-reference capture can use the default capture mode, where the compiler automatically analyzes which external variables the lambda needs to capture. The default capture modes for by-value and by-reference can be mixed, as shown below:

[=] // default capture by value
[&] // default capture by reference
[&, factor] // capture by reference, except for factor which is captured by value
[=. factor] // capture by value, except for factor which is captured by reference

The capture list does not allow duplicate declarations, including duplicate capture modes and duplicate variables:

[&, &factor] // error, & has already declared the default capture mode. &factor is duplicated
[=, factor] // same as above
[factor, factor] // error, variable is duplicated

Automatic capture provides a more convenient capture mechanism, but it may unintentionally capture unnecessary variables, which may increase memory usage or introduce performance issues.

The choice between by-value and by-reference capture generally depends on specific scenarios, and in general, the rules for choosing between by-value and by-reference capture are similar to those for normal function parameters.

The capture list can also use variadic templates:

[arg...]

When using a lambda expression in a member function body, the this pointer needs to be explicitly captured to provide access to the member functions and data of the class.

[this, filter]

C++14 introduced a new default capture mode that allows introducing and initializing new variables in the capture clause without putting them inside the closure. This is useful for variables that need to consider scope and lifetime:

auto p = make_unique<T>(factor);

auto _lambda = [ptr = move(p)]{}

Parameter List#

The parameter list of a lambda behaves similarly to the parameter list of a regular function in most aspects.

Both the capture list and the parameter list can be used to provide variables to the closure, but the capture list is suitable for variables that are known and will not change in subsequent calls when defining the lambda.

The capture list is suitable for variables that remain unchanged throughout the entire lifetime of the lambda, while the parameter list is suitable for variables that are different each time the lambda is called.

std::vector<int> num = {1,2,3,4};
int fixedValue = 10;

std::for_each(num.begin(). num.end(), [fixedValue](int &x){x += fixedValue;});

In the parameter list, if the parameter type is generic, use auto to inform the compiler to create a template:

[](auto factor)

Mutable#

By default, lambda function calls are const-by-value, but with the addition of the mutable specifier, the closure can modify the captured variables:

Exception#

throw() is used to indicate that the lambda may throw an exception, it can be omitted, or noexcept can be used to indicate that no exceptions are thrown:

[]() noexcept {}
[]() throw() {}
[]() {}

Return Type#

The return type in a lambda can be omitted, and the compiler will automatically infer it, but there are exceptions:

// C++11 and earlier, some compilers
[](int i){
  if (i < 0){
    return i;
  }
  else{
    return -i;
  }
} // error, inferred type is void

This is due to the inference rules of the operator() of the closure:

If the body contains only a single return expression, the return type is the type of the expression; otherwise, the return type is void. (Before C++14).

Lambda Body#

The basic behavior is consistent with regular functions.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.