Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++14

C++14 Lambdas: You've Got In-place Anonymous Functions

4.67/5 (27 votes)
1 May 2016CPOL4 min read 34.7K  
A demonstration of how to use C++11/C++14 lambdas / Closures

Introduction

C++ Lambdas (C++11/14) are used widely for writing in-place functions and generally enhances the functionality of C++. Though in-place anonymous function definition is relatively new to C++ as compared to many other languages, it is extremely easy to make use of once understood properly

Background

C++ lambdas owing to its syntax is bit tedious to understand at first even for experienced C and C++ programmers. while for people with experience in languages like JavaScript, Python, etc., it's easy to understand the concerpt but still the syntax is a bit tricky.

This article is expected to clear the clouds on usage of C++ lambdas.

Section 1: Lambda Syntax

The syntax of lambda consists of 3 items:

  1. [] Open Close square brackets
  2. () Open Close round brackets
  3. {} Open Close curly brackets

This statement is syntactically valid C++ lambda and will compile successfully:

C++
int main() {
         [](){};
         return 0;
}

while it may look weired in the beginning, we'll discuss capture and argument list ([]()) later. Let's first understand how to make lambda functional.

The functional block can be added in lambda by writing code in between open and close curly brackets as we do for any other functions in C/C++.

C++
int main() {
         []() {
               cout << "Lambda calling this cout" << endl;
          }
         return 0;
}

The lambda does have functionality but it will not call itself automatically and we need to explicitly call it. There are two ways to call a lambda:

  1. In Place calling
  2. Call by giving a name to it

We'll see both versions in this article, first the in place calling which is similar to calling any function by using ( ).

C++
int main()
{
    []() {
        cout << "Lambda calling this cout" << endl;
    }(); // it will call this lambda in place

    return 0;
}

Similarly, function parameter can be passed inside lambda by indicating these parameters inside ( ). Remember while calling in place we need to provide the parameter argument, otherwise a compiler error will be generated.

C++
int main()
{
    [](int val) {
        cout << "The value passed in this function is ->" << val << endl;
    }(100); // Passing the parameter while calling in place

    return 0;
}

Section 2: Giving Name to a Lambda Function

The lambdas can be given a name so that they can be used at any later point of time. Only for anonymous lambdas, usage has to be done in-place as described in Section 1.

There are 3 possible ways to give lambda a name.

2.1: Using C++ Auto Keyword

The lambda can be simply assigned a name by using C++ auto keyword. By far, this is the simplest way of assigning a name to a lambda.

C++
int main()
{
    auto lfn = []() {
        cout << "This lambda is called using the names" << endl;
    };
    // call the lambda function
    lfn();

    return 0;
}

2.2: Using std::function<>

The "auto" keyword automatically generates the function pointers. However, we can explicitly use std::function<> to hold that function pointer. The code snippet below shows usage of std::function<>. The function below takes integer as function argument.

C++
int main()
{
    std::function<void(int)> lfnfunc = [](int val) {
        cout << "The value passed in this function is ->" << val << endl;
    };
    // calling lambda function with explicit std::function handler (replacement of auto)
    lfnfunc(200);

    return 0;
}

2.3: Using Plain 'C' Style Function Pointers

Lambdas are just functions and can be assigned to a 'C' style function pointers. Though I'll seriously discourage you to use this:

C++
int main()
{
    // using plain c style function pointers
    void(*cstylefp)(int) = [](int val) {
        cout << "The value passed in this function is ->" << val << endl;
    };
    // calling the function pointers
    cstylefp(300);

    return 0;
}

Section 3: Lambda Return Types

Just like normal functions, lambdas can also return values. The return value is provided after round brackets and before curly brackets. The return types are specified with Arrow (->) sign in lambdas.

C++
int main()
{
    //lmabda returning int
    int retval = []() -> int { return (int)1; }();
    return 0;
}

Just like the above, we can also give name to a lambda function with return types. As explained above, the best way to use the same is "auto" keyword.

C++
int main()
{
    // Giving name to a lambda with return type
    auto lfnParam = [](int val) ->int {
        cout << "lambda takes and interger ->" << 
        val << "<- and returns an Integer" << endl;
        return val * 100;
    };

    // calling lambda function with parameter and collecting the return value
    int retval1 = lfnParam(100);
    return 0;
}

Section 4: The lambda Capture List

The capture list is one of the unique properties of lambda and helps it behave like closures in other languages like JavaScript.

The capture list is mainly used to pass local variables to the lambda functions without explicitly using them as part of parameters list, i.e., inside round brackets.

The parameters can be passed by value, reference or a combination of both.

4.1: Passing the Locals by Value

Local variables can be passed into lambda function by specifying the same in the capture list. We can then access the variables inside the lambda function.

In the code below, we are accessing the local variables 'x' and 'y' inside the lambda function.

C++
int main()
{
    int x = 10, y = 20;
    // Passing x & y in lambda by specifying the same in capture list
    auto retVal = [x, y]() -> int 
    { 
        return x + y; 
    }();

    cout << "retVal => " << retVal << "  
    x => " << x << "   y => " << y << endl;

    return 0;
}

4.2: Passing the Locals by Reference

Local variables can also be passed by reference in the capture list. If passed by reference, the update to local variables will be reflected outside lambdas.

In the code below, we are accessing local variable passed by reference and modifying the same:

C++
int main()
{
    int x = 10, y = 20;
    // Passing x and y in lambda as reference in capture list and modifying the same
    auto retVal1 = [&x, &y]() -> int { x++; y++; return x + y; }();
    cout << "retVal1 => " << retVal1 << "  
    x => " << x << "   y => " << y << endl;

    return 0;
}

4.3: Passing All Local Variables By Value (At once)

There are situations where all local variables need to be passed by value, we have a way where we can do the same without explicitly mentioning each one of them. We can do it via using '=' sign in capture list as shown in the sample code below:

C++
int main()
{
    int x = 10, y = 20, z = 30;
    // passing all locals to lambda by value

    auto retval2 = [=]() -> int { return x + y + z; }();
    cout << "retVal2 =>" << retval2 << endl;

    return 0;
}

4.4: Passing All Local Variables by Reference (At Once)

In situations where all local variables need to be passed by reference, we can do it via using '&' sign in capture list as shown in the sample code below:

C++
int main()
{
    int x = 10, y = 20, z = 30;
    // passing all locals to lambda by reference
        auto retval3 = [&]() -> int 
        {    x++; y++; z++; 
            return x + y + z; 
        }();
    cout << "retVal3 =>" << retval3 << "  
    x => " << x << "  
    y =>" << y << " z=> " << z << endl;
    return 0;
}

4.5: Passing Some Local Variables by Value and Some by Reference

The variables can be passed in capture list in mixed and match way, where some variables can be passed by value and some by Reference as depicted in code below:

C++
int main()
{
    int x = 10, y = 20;
    // passing locals in mix and match way ('x' by value and 'y' by reference)
    auto retval4 = [x, &y]() -> int 
            { y++; 
              return x + y; 
    }();
    cout << "retVal4 =>" << retval4 << "  
    x => " << x << "  y =>" << y << endl;
    return 0;
}

Note 1: We cannot modify pass by value variables, i.e., we can't do x++; in the above example.

Note 2: lambda capture works only for local scope and cannot be used for global variables.

Points of Interest

The lambdas especially the capture lists make some task easy, especially when you want to achieve some functionality in nested function.

History

  • First version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)