Home C++ Introduction Decisions Loops Input/Output Functions Stack and Heap References Arrays Searching and Sorting Recursion Pointers Character and Strings Structures Classes Inheritance Exceptions Templatess STL Modern C++ Misc Books ----

Lambdas


Contents

Capture Clause

The capture clause can specify how we can include variables from outside the function.
[=] (parameters) mutable  throw()   -> return-type {body}
1      2            3       4             5           6
1) capture clause
2) parameters
3) mutable
4) throw
5) return type
6) body
A lambda expression translates to a function object with a class and the operator "()" in the class. It's a more convenient notation than creating a class or an actual function. It can be used inline or used as arguments. We can use the handy "auto" to declare the type that the lambda expression will return.
1) capture clause
This dictates the variables that we can access before
the lambda function.



File: lambdas1.cpp
#include <algorithm>
#include <array>
#include <string>
#include <functional>
#include <iostream>

using namespace std ;


class Integer {
public:
    int x1 ;
    Integer(int x1p)
    {
        x1 = x1p ;
        cout << "Object Created" <<
         x1 <<  endl;

    }


    Integer(const Integer& param1)
    {
        x1 = param1.x1 ;
        cout << "Copy constructor: Object Created" <<
           x1 <<  endl;
    }


    ~Integer()
    {
        cout << "Object Destroyed: " << x1 <<  endl;
    }
};



int main()
{
   int x1 = 10 ;
   int x2 = 10 ;
   //outside variable like x1 is not captured
   //auto print1 = [](string str1){
       auto print1 = [](string str1){
              cout << str1 << endl ;
       } ;

   print1( "Calling the lambda print1 function." ) ;

   //Trying to access the variable outside the
   //scope

   auto print2 = [](string str1){
              cout << str1 << endl ;
             // cout << x1 << endl ;
       } ;

   print2( "Calling the lambda print2  function." ) ;


   auto print3 = [=](string str1){
              cout << str1 << endl ;
              cout << x1 << endl ;
              //x1=5 ; Compiler error
              //x1 is read only
       } ;

   print3( "Calling the lambda print3  function." ) ;


   auto print4 = [&](string str1){
              cout << str1 << endl ;
              x1=4 ;

       } ;

   print4( "Calling the lambda print4  function." ) ;
   cout << x1 << endl ;

   auto print5 = [x1](string str1){
              cout << str1 << endl ;
              cout << x1 << endl ;
              //cout << x2 << endl ;
              //The "x2" is not accessible here.
              //[&x1] will pass x1 by reference
       } ;

   print5( "Calling the lambda print5  function." ) ;


   Integer obj1( 10 ) ;

   //A copy is created inside the lambda
   //Copy constructor gets called.
   //When print6 goes out of scope then
   //the obj1 gets destroyed.

   auto print6 = [=](string str1){
              cout << str1 << endl ;
              cout << obj1.x1 << endl ;
              //We cannot modify obj1
       } ;

   print6( "Calling the lambda print6  function." ) ;

  //capture all the variables by reference
  //Copy constructor does not get called.
   cout << "-------------------" << endl ;
   auto print7 = [&](string str1){
              cout << str1 << endl ;
              cout << obj1.x1 << endl ;
              obj1.x1 = 5 ;
       } ;

   print7( "Calling the lambda print7  function." ) ;

   cout << "-------------------" << endl ;
   //Allowing access to a single variable/object
   //Another copy of obj1 gets created here.

   auto print8 = [obj1](string str1){
              cout << str1 << endl ;
              cout << obj1.x1 << endl ;
             // Other variables are not captured
             // cout << x1 << endl ;
       } ;

   print8( "Calling the lambda print8  function." ) ;


   auto print9 = [obj1, x1](string str1){
              cout << str1 << endl ;
              cout << obj1.x1 << endl ;
             // Other variables are not captured
              cout << x1 << endl ;
       } ;
   print9( "Calling the lambda print9  function." ) ;




   cout << "-------------------" << endl ;
   //Passing a single variable by reference
   auto print10 = [&obj1](string str1){
              cout << str1 << endl ;
              //Allowed
              obj1.x1 = 100 ;
              cout << obj1.x1 << endl ;
       } ;

   print10( "Calling the lambda print10  function." ) ;
   //Changes are preserved after the invocation
   cout << obj1.x1 << endl ;
   cout << "-------------------" << endl ;


   cout << "-------------------" << endl ;
   cout << "mix and match example" << endl ;

   //we can mix and match
   auto print11 = [&obj1, x1](string str1){
              cout << str1 << endl ;
              cout << x1 << endl ;
              cout << obj1.x1 << endl ;
       } ;

   print11( "Calling the lambda print11  function." ) ;
   cout << "-------------------" << endl ;

   auto print12 = [=, &obj1](string str1){
                 cout << str1 << endl ;
                 cout << x1 << endl ;
                 //x1 = 13 ; compiler error
                 //x1 is only captured by value
                 obj1.x1 = 12 ;
                 cout << obj1.x1 << endl ;
          } ;

   print12( "Calling the lambda print12  function." ) ;
   cout << "-------------------" << endl ;



}
In the above file we see the statement:

   auto print1 = [](string str1){
              cout << str1 << endl ;
	   } ;

   print1( "Calling the lambda print1  function." ) ;

We are using the handy "auto" to define the type for the
lambda expression. With this keyword the system will determine the
type for us. As we will see later the lambda is essentially a
functor object.

We are not trying to access the variables defined in the scope above the
lambda definition.

   auto print2 = [](string str1){
              cout << str1 << endl ;
             // cout << x1 << endl ;
	   } ;

   print2( "Calling the lambda print2  function." ) ;

The capture close is empty ( "[]" ). So if we try
to access "x1" then we get a compiler error.

   auto print3 = [=](string str1){
              cout << str1 << endl ;
              cout << x1 << endl ;
              //x1=5 ;
              //x1 is read only

	   } ;

   print3( "Calling the lambda print3  function." ) ;

Here we place the equal sign in the capture clause "[=]".
This means we have access to the other variables by value.
We cannot change the values as the "x1" is considered read only.
If we do want to change the value then we need to pass "x1" by
reference.

   auto print4 = [&](string str1){
              cout << str1 << endl ;
              x1=4 ;

	   } ;

   print4( "Calling the lambda print4  function." ) ;
   cout << x1 << endl ;
We see in the output that the value of "x1" change was retained.

Calling the lambda print4  function.
4

   auto print5 = [x1](string str1){
              cout << str1 << endl ;
              cout << x1 << endl ;
              //cout << x2 << endl ;
              //The "x2" is not accessible here.
	   } ;

   print5( "Calling the lambda print5  function." ) ;

We can choose to make only certain variables accessible
to the lambda expression. In the above we are making "x1"
accessible but denying access to any other variables such
as "x2".

   Integer obj1( 10 ) ;

   //A copy is created inside the lambda
   //Copy constructor gets called.
   //When print6 goes out of scope then
   //the obj1 gets destroyed.

   auto print6 = [=](string str1){
              cout << str1 << endl ;
              cout << obj1.x1 << endl ;
              //We cannot modify obj1
	   } ;

We can also choose to capture objects.In the above we are capturing
the object "obj1" and another copy gets created in the lambda
expression even though the object name stays the same which is "obj1".
We can see that because the "copy constructor" gets called when we
invoke this lambda object.

The same name strategy works fine because we are not allowed
to modify "obj1".
Of course in order to modify "obj1" we need to use the reference
clause.
  //capture all the variables by reference
  //Copy constructor does not get called.
   cout << "-------------------" << endl ;
   auto print7 = [&](string str1){
              cout << str1 << endl ;
              cout << obj1.x1 << endl ;
              obj1.x1 = 5 ;
	   } ;

   print7( "Calling the lambda print7  function." ) ;


   //Allowing access to a single variable/object
   //Another copy of obj1 gets created here.

   auto print8 = [obj1](string str1){
              cout << str1 << endl ;
              cout << obj1.x1 << endl ;
             // Other variables are not captured
             // cout << x1 << endl ;
	   } ;
   We can choose to allow access to just a single object as the
   above snippet shows. We could also place more variables in the
   capture clause separated by commas.


   auto print9 = [obj1, x1](string str1){
              cout << str1 << endl ;
              cout << obj1.x1 << endl ;
             // Other variables are not captured
              cout << x1 << endl ;
	   } ;
   print9( "Calling the lambda print9  function." ) ;

   Passing the object by reference.

   auto print10 = [&obj1](string str1){

   We can choose to make some variable as pass by reference
   and others pass by value.
   //we can mix and match
      auto print11 = [&obj1, x1](string str1){
                 cout << str1 << endl ;
                 cout << x1 << endl ;
                 cout << obj1.x1 << endl ;
   	   } ;

   print11( "Calling the lambda print11  function." ) ;
   cout << "-------------------" << endl ;

   In the below we are stating that "obj1" is passed by reference
   and all other parameters are passed by value.

   auto print12 = [=, &obj1](string str1){
                 cout << str1 << endl ;
                 cout << x1 << endl ;
                 //x1 = 13 ; compiler error
                 //x1 is only captured by value
                 obj1.x1 = 12 ;
                 cout << obj1.x1 << endl ;
   	   } ;

   print12( "Calling the lambda print12  function." ) ;
   cout << "-------------------" << endl ;

Exercise



File: lambdas_ex_1.cpp
#include <algorithm>
#include <array>
#include <string>
#include <functional>
#include <iostream>

using namespace std ;

class Person
{
    public:
     int age ;
     int id ;
    Person(  int idP , int ageP )
     {
        age = ageP ;
        id = idP ;
     }
    Person(const  Person& obj1 )
     {
        cout << "Copy constructor." << endl ;
        age = obj1.age ;
        id = obj1.id ;
     }

    ~Person()
     {
         cout << "Person Destructor." << endl ;
     }
};


void swapM( int& x1, int& x2 )
{
  int temp ;
  temp = x1 ;
  x1 = x2 ;
  x2 = x1 ;
}

int main()
{
   int a1 = 10 ;
   int a2 = 11 ;
   //TO DO Write a lambda expression to replace swapM
   //The capture clause [] is empty
          auto swap1 =

       swap1(a1,  a2 ) ;
       cout << "a1:" << a1 << " " << "a2:" << a2 << endl ;


   Person person(1 , 50  ) ;

   //Pass this object ( "person") by value to a lambda and print
   // the id and age.
    { //TO DO Write the code for the lambda in the curly braces
       //The capture clause is not empty.
      //Does the copy constructor get called. When does the object
      //in the lambda get destroyed ?
       auto print1 =
       print1() ;
    }

   //Pass this object ( "person") by reference to a lambda and print
   // the id and age. How is this different from the calling by
   //value above ?
   //The capture clause is not empty.
     auto print2 =
     print2() ;

}
2) The below program produces a warning. Fix it .

File: lambdas_ex_2.cpp
#include <iostream>

int global_var = 10; // A global variable

int main()
{
    auto lambda_func = [global_var]()
    {

        std::cout << "Value of global_var: " << global_var << std::endl;
    };

    lambda_func(); // Call the lambda

    return 0;
}
















































Solutions



File: lambdas_ex_1s.cpp


File: lambdas_ex_2s.cpp