Go back to Richel Bilderbeek's homepage.

Go back to Richel Bilderbeek's C++ page.

 

 

 

 

 

(C++) lambda expressions

 

The use of lambda expressions is a technique that can be used, depending on the standard used:

 

The speed of C++98 Boost.Lambda and C++11 lambda expressions are compared and profiled (for only one simple function) in Answer of exercise #7: AddOne.

 

 

 

 

C++98 lambda expression in the C++98 standard

 

lambda expressions are not directly supported by the C++98 standard. Boost.Lambda, however, can be used instead:

 

#include <algorithm>
#include <cassert>
#include <vector>
#include <boost/lambda/lambda.hpp>
#include <boost/bind.hpp>

void AbsSort(std::vector<int>& v)
{
  std::sort(
    v.begin(),
    v.end(),
      boost::bind(&std::labs,boost::lambda::_1)
    < boost::bind(&std::labs,boost::lambda::_2) );
}

int main()
{
  //C++11 initializer list technique
  std::vector<int> v = { 4,-3,2,-1 };
  AbsSort(v);
  assert(v[0] == -1);
  assert(v[1] ==  2);
  assert(v[2] == -3);
  assert(v[3] ==  4);
}

 

 

 

 

 

C++11 lambda expression in the C++11 standard

 

Prefer a named function object to a lambda expression if the operation requires comments [3]. Prefer a named function object to a lambda expression if the operation is generally useful [4]. Keep lambda expressions short [5]. For maintainability and correctness, be careful about capture by reference in a lambda expression [6]. Let the compiler deduce the return type of a lambda expression [7].

 

 

lambda expressions can be used for on-the-fly functors:

 

#include <algorithm>
#include <cassert>
#include <vector>

void AbsSort(std::vector<int>& v)
{
  std::sort(
    v.begin(),
    v.end(),
    [](const int a, const int b) { return std::abs(a) < std::abs(b); } );
}

int main()
{
  //C++11 initializer list technique
  std::vector<int> v = { 4,-3,2,-1 };
  AbsSort(v);
  assert(v[0] == -1);
  assert(v[1] ==  2);
  assert(v[2] == -3);
  assert(v[3] ==  4);
}

 

Let's zoom in at the lambda expressions:

 

  std::sort(v.begin(),v.end(),
    [](const int a, const int b) { return std::abs(a) < std::abs(b); } );

 

The '[]' denotes that there is an empty capture list: the lambda expressions does not need to be fed a constant from its environment. The '(const int a, const int b)' are the names and data types to lambda expressions works on: for sorting one needs two arguments: these are called 'a' and 'b' and are const integers. Finally, the '{ return std::abs(a) < std::abs(b); }' describes how the arguments are used to draw a conclusion.

 

The example below does not show a capture list, the example below does:

 

//C++11 lambda expression
//From http://www.richelbilderbeek.nl/CppAdd.htm
void Add(std::vector<int>& v, const int x)
{
  std::for_each(v.begin(),v.end(),[x](int&i) { i+=x; } );
}

 

In the example above, 'x' is in the capture list: the capture list consists of variables that are needed in the lambda expressions.

 

 

 

 

 

C++11 Program flow of a lambda expression in the C++11 standard

 

 

Program flow in a C++11 lambda expression differs from a C++98 lambda expression or BOOST_FOREACH: if you want to return from a function, all that happens is that the std::for_each (or other algorithm) is terminated. The example below shows this.

 

#include <algorithm>
#include <cassert>
#include <iostream>
#include <vector>

void TestProgramFlow()
{
  //2-D std::vector, note 42 in the middle, from an initializer list
  const std::vector<std::vector<int> > v
    =
    {
      {  0, 1, 2, 3, 4 },
      { 10,11,12,13,14 },
      { 40,41,42,43,44 },
      { 50,51,52,53,54 },
      { 60,61,62,63,64 }
    };
  //First lambda expression
  std::for_each(v.begin(),v.end(),
    [](const std::vector<int>& w)
    {
      //Nested second lambda expression
      std::for_each(w.begin(),w.end(),
        [](const int i)
        {
          if (i == 42)
          {
            std::cout << "FOUND!\n";
            return; //Terminates the second lambda expression,
                    //Does not return from Test function
          }
        }
      );
    }
  );
  //Will get here, as the return statement only terminates
  //the second lambda expression, instead of the Test function
  assert(!"Should not get here");
}

 

Screen output:

 

FOUND!

 

 

 

 

 

Advice

 

 

 

 

 

 

References

 

  1. GCC page about C++0x support
  2. Aaron Ballman's blog about C++0x
  3. Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 11.6. Advice. page 303: '[6] Prefer a named function object to a lambda if the operation requires comments'
  4. Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 11.6. Advice. page 303: '[7] Prefer a named function object to a lambda if the operation is generally useful'
  5. Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 11.6. Advice. page 303: '[8] Keep lambdas short'
  6. Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 11.6. Advice. page 303: '[9] For maintainability and correctness, be careful about capture by reference'
  7. Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013. ISBN: 978-0-321-56384-2. Chapter 11.6. Advice. page 303: '[10] Let the compiler deduce the return type of a lambda'
  8. Scott Meyers. C++ And Beyond 2012 session: 'Initial thoughts on Effective C++11'. 2012. 'Prefer Lambdas over Binders'

 

 

 

 

 

Go back to Richel Bilderbeek's C++ page.

Go back to Richel Bilderbeek's homepage.

 

Valid XHTML 1.0 Strict