Playing with C++ lambda

With the new version of C++ we got a chance to start using lambda functions. It's a great way to simplify the code. Imagine this traditional sorting scenario:

bool comparator(int a, int b)
{
    return a < b;
}

void sort(std::vector<int>& array)
{
    std::sort(array.begin(), array.end(), comparator);
}

With lambda we can simplify it into one function:

void sortLambda(std::vector<int>& array)
{
    std::sort(array.begin(), array.end(), [](int a, int b) -> bool
    {
        return a < b;
    });
}

Pretty cool, now let's try something more interesting. Lambda function is just a function without name, so it can't be declared in a traditional way. But it can be used as a variable:

auto lambda = [](std::string param) { std::cout << param; };

lambda("Hi!");

Lambda function can't be used as a type directly because the type is unknown, but it can be used in a combination with std::function<> template. This way we can declare a type and use it as a class member:

struct Object
{
    void registerCallback(const std::function<void(std::string)>& callback)
    {
        m_callback = callback;
    }

    std::function<void(std::string)> m_callback;
};

Now we use the Object to register lambda as a callback:

Object object1;
object1.registerCallback(lambda);
object1.m_callback("Hi!");

Or we can declare and register the lambda at the same time:

Object object2;
object2.registerCallback([](std::string param)
{
    std::cout << "Object 2: " << param;
});

object2.m_callback("Hi!");

Another nice example of using lambda is when creating a thread. Now it can be done in one line call!

std::thread thread([]()
{
    std::cout << "Hi from the other thread!";
});

Now when you want to use an external object inside the lambda scope, you can capture it inside the brackets. Lambda capture is a mechanism for passing an external variable to the lambda scope. A typical example is to capture this as a class instance pointer:

auto lambda = [this]()
{
    // now I can access any member of this class
};

Variables can be captured by pointer, reference or name. However we must be careful about validity of captured objects, because lambda does not extend their lifetime. For example this will crash:

auto object = new Object();
auto lambda = [object]()
{
    object->sayHi();
};

// ....

delete object;
object = nullptr;

lambda(); // crash! captured object was deleted

Similar problem will happen when passing local variable by reference. When the lambda is called after exiting the variable scope the capture becomes a dangling reference:

std::function<void()> lambda;

{
    auto number = 4;

    lambda = [&number]()
    {
        std::cout << number;
    };
}

//! now the captured "number" became a dangling reference
lambda();

After understanding these simple rules there are no problems of using lambda. In fact after some time I don't see any reason why NOT to use lambdas. The way how it simplifies the code is just amazing. I can say that it completely changed the way how I code and think about program flow and architecture. It's a very fresh wind to C++ language.

I have to recommend one excellent book from Scott Meyers Effective Modern C++ for more in depth examples and explanations behind lambdas and other techniques.