IBM Z and LinuxONE - Languages - Group home

Lambda-expressions in C++14

  

Standard C++14 makes C++11 more complete as C++11 has a lot of imperfections. C++14 not only amends C++11 but also proposes many new features, among which the most eye-catching one is the supplements to lambda-expressions.


Lambda-expression is the most interesting feature in C++11 that challenges the long-used way of defining functions. C++14 proposes two major supplements to this famous feature.

 

The first supplement is called “generalized lambda-capture” that comes from proposals N3610 and N3648. This supplement is initially proposed for capture-by-move. Besides this new functionality, the supplement also makes capture lists more flexible. This supplement indicates that expressions are now allowed to appear in the capture lists of lambda-expressions. See the following example:

   int x = 4;

   auto y = [&r = x, x = x+1]()->int {

       r += 2;

    return x+2;

   }(); // Updates ::x to 6, and initializes y to 7.


In this example, you can find two variables r and x in the capture list. Variable r is captured by reference and initialized with the value of x. However, r is not declared in the parent scope of the lambda-expression, which is not permitted in C++11. In C++14, its type is deduced by its initialization expression. Here, r is an lvalue reference of x. The other variable x is declared in the parent scope of the lambda-expression, and it is captured by value and initialized with the value of x+1, so it is not the same x in the parent scope. The initial value of x in the lambda-expression is 5. After calling the above lambda-expression, we can get the final results of x and y that are 6 and 7 respectively.


The compiler achieves lambda-expressions by converting them to function objects. To help you better understand this new feature of lambda-expressions, you can try to convert the above lambda-expression to a function object.

 

The other supplement to lambda is called “generic lambda” that comes from N3559 and N3649. In C++14, the auto type specifier can be the specifier of lambda parameters to identify a generic lambda-expression. See the following example:

   auto Identity = [](auto a) { return a; };

   int three = Identity(3);

   char const* hello = Identity("hello");


In this example, we define a generic lambda-expression and initialize Identity with the closure object. In the implementation, the compiler converts this generic lambda-expression to the following function object:

   template<typename T>

   class lambda {

   public:

       T operator(T t) { return t; }

   };


When function Identity(3) is called, the generic lambda-expression is initialized to a lambda-expression whose parameter type is int. When Identity (“hello”) is called, the generic lambda-expression is initialized as its parameter type is const char*. Generic lambda expressions are acting as function templates.

 

A generic lambda-expression without a capture list can be converted to a pointer-to-function. When the pointer-to-function is invoked, it has the same effect as invoking the generic lambda-expression. See the following example:

   auto Identity = [](auto a) { return a; };

   int (*fpi)(int) = Identity;


In both C++11 and C++14, if a lambda-expression has a capture list, it cannot be converted to a pointer-to-function. This is a big difference between a lambda-expression and a traditional function: the former can have initial status while the latter cannot.