C++11: Lambda expressions

Consider you have a Person class:

class Person
{
  private:
    string firstName;
    string lastName;
    int id;

  public:
    Person(const string& fn, const string& ln, int i)
         : firstName(fn), lastName(ln), id(i) { }

    const string& getFirstName() const { return firstName; }
    const string& getLastName() const { return lastName; }
    int getID() const { return id; }
};


You want to store several instances of this class in a vector:

vector<Person> people;
people.push_back(Person("Davor", "Loayza", 62341));
people.push_back(Person("Eva", "Lopez", 12345));
people.push_back(Person("Julio", "Sanchez", 54321));
people.push_back(Person("Adan", "Ramones", 70000));

If you want to sort this vector by the person ID, you could implement a PersonComparator and use the STL sort algorithm:

class PersonComparator
{
  public:
     bool operator()(const Person& p1, const Person& p2) const
     {
        return p1.getID() < p2.getID();
     }
};

PersonComparator pc;
sort(people.begin(), people.end(), pc);

In C++03 you need to create a separate class (or alternatively a function) to use the sort algorithm (actually to use any STL algorithm). C++0x introduces the “lambda expressions”, that are a nice way to implement the functionality passed to the algorithm when it is going to be used. So, instead of declaring the PersonComparator as shown above, you could implement the same functionality in this way:

sort(people.begin(), people.end(), [](const Person& p1, const Person& p2)
{
  return p1.getID() < p2.getID();
});

Quite simple and easier to read. The “[]” angle brackets are used to mark the extern variables that will be used in the lambda context. “[]” means: “I do not want my lambda function to capture anything”; “[=]” means: “everything passed by value” (thanks Jeff for your clarification on this!!); “[&]” means: “everything passed by reference”.

Given the vector declared above, what if you want to show all instances inside it, you could do this in C++03:

ostream& operator<<(ostream& os, const Person& p)
{
  os << "(" << p.getID() << ") " << p.getLastName() << "; " << p.getFirstName();
  return os;
}

class showall
{
  public:
    void operator()(const Person& p) const { cout << p << endl; }
};

showall s;
for_each(people.begin(), people.end(), s);

And if you would want to show the instances with ID between 20000 and 60000 only, you could do this:

class showrange
{
  private: int min; int max;

  public:
    showrange(int m, int n) : min(m), max(n) { }
    void operator()(const Person& p) const
    {
       int id = p.getID();
       if (id >= min && id <= max)
         cout << p << endl;
    }
};

showrange sr(20000, 60000);
for_each(people.begin(), people.end(), sr);

And with lambdas you could do both examples in this way:

for_each(people.begin(), people.end(), [](const Person& p)
{
  cout << p << endl;
});

int min = 20000, max = 60000;
for_each(people.begin(), people.end(), [min, max](const Person& p)
{
   int id = getID();
   if (id >= min && id <= max)
     cout << p << endl;
});
Advertisements

9 thoughts on “C++11: Lambda expressions

  1. i would love to be able to use auto inside of the lamda expression argument list … something like

    std::for_each(a.begin(), a.end(), [](auto& i){ i *= 5;});
    

    but that *does not* work :(
    is there a solution for me?

    1. Using auto as part of the argument list is not allowed, so, your example wouldn’t work; but, what if you do something like this:

      multiplyByN(a, 5);

      and implement it in this way:

      template <typename T>
      void multiplyByN(vector<T>& v, int n) //supposing "a" was a vector<T>
      {
        for_each(v.begin(), v.end(), [](T& i) { i *= n; });
      }
      

      It works perfectly!

      1. thanks for the quick response :)
        well the example i provided was just off the cuff but thanks anyways for providing an example …

        actually the beauty for using lambdas, for me at least, was not writing functors or external functions like the one you kindly provided… i wanted to be able to write a lambda just where i needed it which invariably was within a function body. this way i kept all the related code pieces together. i guess the compiler implements lambdas as functors behind the scenes but at least my code looks pristine.

        i was hoping that by using auto within lambda argument list i would move closer to generic programming.

        i was actually trying out a piece of code and i could not figure out whether an iterator was being passed to the unary function or an element so wanted to use auto to take the guesswork out.

      2. so, just out of curiosity, if you can’t use “auto” could you do:

        std::for_each (a.begin(), a.end(), [](&decltype(expression_poster_is_evaluating) i) { i *= 5;});

        instead?

      3. Hi Rino, yeap, you can say something like:

        for_each(v.begin(), v.end(), [](decltype(*(v.begin()) i) { i* = n; });

  2. Hi! Your posts are very informative, but I noticed a small mistake midway through this article.

    Empty brackets mean the lambda takes nothing (I forget the technical term for it), [=] means take everything by value, and [&] means take everything by default (like you said). You can also mix and match what you take by value and what you take by reference.

    I’ve learned a lot from your posts: Thanks again!

    -Jeff

    1. Thanks for your nice comment Jeff and for your clarification. I already updated the post fixing the error. Thanks for reading me :)

    1. Ohh! sorry! yes, of course you are completely right! :) I’ll update that error right now!

      Thanks for reading me! :)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s