Lambdas: What are they and When to use them!

I’ve always know that Lambdas existed and had some idea as to what they were (inline functions right?); but I never really bothered to learn how to use them (and the few times I came across them i had no idea what I was looking at). It wasn’t until recently, while reading about the new c++11 features, that I finally came to understand what they are and how to use them.  With my more recent discovery that (unlike most new c++11 features) they can be used in Visual Studio 2010 I thought I’d share my new insight.

Because it took me a while to get my head around them I’ve tried to make this explanation as simple as possible so a relative newcomer to c++ can understand them. However I will point out that if you don’t know what function pointers are and how to use them your going to struggle with the lambdas.

Lambda Functions: What are they?

A lambda function is a way to create a function inline where you need it, rather than having to create a function somewhere else and specify it later. Without lambdas you’d need to do something like the following to get a function pointer:

int Add(int a, int b)
{
     return a + b;
}

// somewhere else:
typedef int (*AddFunc)(int, int);

// another place:
AddFunc addFuncPointer = Add;

This is a real pain, especially for a trivial function that is only going to be used once. With a lambda you can just go:

// another place:
auto addFuncPointer = [] (int a, int b) { return a + b; }; // our lambda

Much simpler right?

Braking down the example above there are basically three parts to a lambda, the capture [], the arguments (), and the body {}. You’ll notice also that there’s no name, this is because a lambda function is designed to be used where it is written, not reused through a program like a traditional function.

Capture what?

The square brackets [] at the beginning of the lambda allow us to capture local variable (thus the ‘capture’). What do I mean by capture? well it allows us to do stuff like this:

int count = 0;
auto countIt = [&count] () { return ++count; };

In the above we are using [] to “Capture” the local variable count so we can use it in the body of our lambda. Also by using the & symbol we are telling the compiler that we want to capture count by reference. In addition to capturing by reference we can also capture by value [=count] or [count]. if you wanted to capture multiple variables you can do so by separating them by a comma:

[=count, &myArray, =somethingElse] () { /* do stuff */ };

You’ll also notice that you can mix and match capturing by Value/reference too. Want to capture everything? Well you can do that by using [&] or [=] to capture all local variable by reference or value respectively. To capture nothing just leave the square brackets empty: [].

Arguments

Yep arguments, you know, just like every other function you’ve ever written ;).

Theres not much more to say about the arguments, they really are exactly like those of a traditional function.

Body

The body of the function is also very just like those of traditional functions. Although you’ll probably want to keep your lambda’s short, say 5 lines or so. Do note that you need to put the semicolon after the closing curly brace like in the examples above.

Return Type

In all the examples I’ve shown so far I’ve been able to leave it up to the compile to reduce the return type, however in a more complex lambda with multiple return statments. So how do we do this? by using another new c++11 feature: Suffix Return Type Syntax. An Example:

[] (enum Arg) -> int {
     switch (Arg)
     {
     case A:
          return 1;
     case B:
          return 2.0f;
     case C:
          return 3.0;
     default:
          return 'D';
     }
 };

In addition to the multiple return statements the above example is also return different types that can be implicitly cast to each other (not exactly best practice I know). It is impossible for the compiler to work out what the return type should be. The -> int syntax after the lambda’s arguments is the suffix return type, it tells the compile that the return type for the lambda is an int. I wont go into full details on the suffix return type syntax here, instead I recommend you read the article linked to above if you want to know more.

When should I use them?

Okay so I’ve explained what a lambda function is and their syntax in c++11 but i haven’t really explained how and when to use them. That’s what this section is for.

STL Algorithms

The STL has contained a serials of algorithms in the <algorithm> header for years now. These are helper functions designed to work with the STL containers. One problem with many of these functions however is that they take a function as one of their parameters and often it is a trivial function that we’ll never use again. Sound familiar? These functions are perfect examples of where a lambda can be put to good use, for example:

std::vector<int> numbers = { 1, 5, 145, 29, 6, 13 };
//...
std::for_each(numbers.begin(), numbers.end(), [] (int a) { ++a });

Notice how we’re writing the lambda directly in place of the last argument? This is what makes lambdas so useful. As is showed earlier in the post if you wanted to pas a function to std::for_each before lambdas came along you’d need to go off and define it some place else (don’t know about you but i find that annoying as anything).

Callbacks

Another place you can use lambdas to good effect is for providing call back functions to other parts of your program. A good example is the TwAddVarCB() function in the AntTweakBar library.  Instead of having to write a specific function and pass a pointer to it to AntTweakBar, you could just right a quick lambda. Here’s an example of using lambdas to provide a callback to the GLFW library:

glfwSetErrorCallback( [] (int a_iError, const char* a_szDiscription) {
printf("GLFW Error occurred, Error ID: %i, Description: %s\n", a_iError, a_szDiscription);
});

It is worth noting that GLFW is a pure C library, despite this the code works just fine.

Anywhere

Its worth noting that a lambda expression yields a function object (or a pointer to a function object). You can use them anywhere you would normally use a function pointer.

For more ideas have a look at this talk by Herb Sutter:

I have been able to find the original slides to go with this talk too, you can see them here. They were originally posted to the Northwest C++ Users Group.

Wrapping up

So that’s it, know you know what lambdas are. If you still don’t get them I’d recommend this article (don’t worry I get it, they confused me for a while too). It’s also worth looking at the official c++ reference page on them too.

Go Nuts, Write Lambdas!

Advertisements

3 thoughts on “Lambdas: What are they and When to use them!”

  1. As you mention glfw: if i try this, it works as expected:
    glfwSetWindowSizeCallback(window,
    [](GLFWwindow * window, int width, int height) {
    std::cout <onResize();
    });

    It fails with “error: cannot convert …”. Could you explain why this is happening? (Happens also with capturing &,= or anything else…) I thought it would be a great Idea to use lambdas instead of the crude workaround where you store your instance in a static member and then call a static wrapper function like
    onResizeCallback(){
    instance->onResize();
    }
    just to use glfwSetWindowSizeCallback(window, onResizeCallback) then…

    Like

    1. First thing, the reason you are getting the “error: cannot convert …” error is because the function object generated by the lambda is not compatible with the definition required by glfwSetWindowSizeCallback(). I don’t know why you are getting the error in the code snippet you provided as it does not occur visual studio 2013. However using a capture will change the resulting function object so as to make it incompatible with the GLFW functions.

      As i understand it you want to use a lambda then call a non-static member function. This won’t work, reason being is that you cannot capture the this pointer (as above) and without it the Lambda is just a stand alone function and has no idea about you class. First this i would do is see if you cannot do away with the member function call and just have the lambda do what is required, or make the member static. If that is not possible then you can use the GLFW user pointer and do some thing like this:

      // specify user pointer:
      glfwSetWindowUserPointer(pGLFWindow, pClassInstance);

      // later Setup our callback:
      glfwSetWindowSizeCallback(window,
      [](GLFWwindow * window, int width, int height) {
      MyClass* pClass = glfwGetWindowUserPointer(window);
      std::cout <onResize();
      });

      Just remember not to leave your user pointer hanging!!
      Hope this helps.

      Like

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