3.1.5 Lambda Capture Expressions

What was the problem

When we are defining a lambda, we have the possibility to capture variables of the enclosing scope such that they become available in the body of the lambda [2.1.7.0]. In practice, such variables are either copied (like var1 below, due to the = syntax), or referenced (like var2 below, with the & syntax).

const auto foo = [=var1, &var2]() -> void {};

Unfortunately there is no other variant of the capture, which leads to situations like this:

void wait_for_new_players(const std::string& group) 
{ 
  std::string display_label = group + ": "; 
 
  // Copies are striking again. Here display_label is copied into a 
  // field of the function object associated with this lambda; not 
  // moved nor initialized on construction. 
  auto add_player_to_list = 
    [display_label](player_id id, const std::string& player alias) 
    { 
      m_player_list->add(id, display_label + alias); 
    }; 
 
  m_team_service.wait_for_players(group, add_player_to_list); 
}

In the above example, we would want to avoid the copy of the display label into the equivalent member variable of the lambda. In C++11 there is only two ways to achieve that: either by creating the display label in the callback’s body, thus creating a new string on each call2, or by using a custom function object, C++98-style.

Additionally, we can note that this behavior prevent any use of a move-only type for a captured variable, such as std::unique_ptr. Indeed, there is no way to move the variable from the outer scope into the lambda.

How the Problem is Solved

In C++14, capture lists have been extended to allow the initialization of a variable by an expression. We can use that to initialize the display label with the final string directly:

void wait_for_new_players(std::string group) 
{ 
  // display_label is initialized directly in the construction of the 
  // lambda, thus saving a copy. 
  auto add_player_to_list = 
    [display_label
=group+":"] 
    (player_id id, const std::string& alias) 
    { 
      m_player_list.add(id, display_label + alias); 
    }; 
 
  m_team_service.wait_for_players(group, add_player_to_list); 
}

This also solves the move-only type situation since we can now move the variable:

void apply_filter(std::vector<int>& values, std::unique_ptr<filter> filter) 
{ 
  std::for_each 
    (values.begin(), values.end(), 
     [f
=std::move(filter)](int& v) -> void 
     { 
       v = f->transform(v); 
     }); 
}

2We would also have a copy of the captured group variable anyway.