C++: Lambda expressions

Having this Person class:

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

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

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

The programmers need to store several instances of this class in a vector:

std::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 they want to sort this vector by person ID, a PersonComparator must be implemented to be used in the std::sort algorithm from the standard library:

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

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

Before C++11, the programmers needed to create a separate class (or alternatively a function) to use the sort algorithm (actually to use any standard library algorithm).

C++11 introduced “lambda expressions”, which are a nice way to implement that functionality to be passed to the algorithm exactly when it is going to be used. So, instead of defining the PersonComparator as shown above, the same functionality could be achieved by implementing it in this way:

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

Quite simple and easier to read. The “[]” square brackets are used to mark the external 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 the programmers want to show all instances inside it? They could do this before C++11:

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

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

show_all sa;
std::for_each(people.begin(), people.end(), sa);

And with lambdas the example could be implemented in this way:

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

11 thoughts on “C++: 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! :)

  3. Nice posts!
    One question: in the last snippet, you added int id = getID();. Shouldn’t it be int id = p.getID(); ?

Leave a reply to awhan Cancel reply