Lambda capture and object lifetime

Someone brought up an issue in comp.lang.c++ today that might be unexpected enough to some that it warrants discussion. Consider the following code

include <functional>
struct up_chuck
{
  int x;

  std::function<int()> get_fun() const
  {
    return [=](){return x;}
  }
};

int main()
{
  auto fun = up_chuck().get_fun();
  
  fun();
}

What do you expect to happen here? If you expect the lambda clause to capture a copy of ‘x’ and create a function that returns it then you’d be quite unfortunately wrong. The issue here is that member variables are not captured directly; the variable that is actually captured is ‘this’! What this means of course is that the lambda captures the ‘this’ pointer by value, making a copy of it, and then using that pointer to access ‘x’ and return it. We can prove this with a different main function:

#include <iostream>
int main()
{
  up_chuck uc;
  uc.x = 0;

  auto fun = uc.get_fun();

  std::cout << fun() << std::endl;
  uc.x = 42;
  std::cout << fun() << std::endl;
}

If you expect that the output of this program should be “0\n0\n” then I suggest you run it and verify that you’ll NOT get that output.

So then, what will happen to our original program if we compile and run it? Well, since the lambda makes a copy of the ‘this’ pointer and accesses its x variable to return it, the behavior of the program is undefined. Since the object that ‘this’ points to is a temporary that is destroyed as soon as the line that creates the ‘fun’ variable is complete, the function that ‘fun’ is now makes use of a dangling pointer.

So what can we do in order to avoid running into this problem? The one thing I can think of is to never, ever use default capture clauses in your lambdas. If we’d written our lambda and attempted to capture ‘x’ by value it would become clear that we’ve got a problem. Then we could assign that value locally and capture that new variable and everything would work fine. If we really do need to capture ‘this’ then we do so explicitly and we know that we’re going to have lifetime issues because we’ve captured a pointer. Thus never using the default capture syntax can at least point out some possible problems to us.

Advertisements

8 Responses to “Lambda capture and object lifetime”

  1. Hello there! This post could not be written any
    better!

    Reading this post reminds me of my old room mate! He always kept chatting

    about this. I will forward this article to him. Fairly certain he will
    have a good read.

    Many thanks for sharing!

  2. Wow! Thank you! I continuously needed to write on my

    website something like that. Can I take a part of your
    post to my site?

  3. A powerful share, I just given this onto a colleague who was doing a little evaluation
    on this. And he actually purchased me breakfast because I
    discovered it for him.. smile. So let me reword that:
    Thnx for the treat! But yeah Thnkx for

    spending the time to debate this, I feel strongly about it and love reading

    extra on this topic. If potential, as you turn into experience, would you thoughts updating your weblog with extra

    particulars? It is extremely helpful for me. Large thumb up for this

    weblog submit!

  4. Hello my friend! I want to say that this article is awesome, nice written and include

    approximately all important infos. I would like to see
    more posts like this.

  5. Hi.

    >> Since the object that ‘this’ points to is a temporary that is
    >> destroyed as soon as the line that creates the ‘fun’ variable is
    >> complete, the function that ‘fun’ is now makes use of a dangling
    >> pointer.

    Who told you that? ‘uc’ is an auto variable which will be destroyed at the end of its scope, i.e. when flow will get closure brace } of main().

    The only undefined behavior have to be in upper example, when you try to access an uninitialized member up_chuck::x

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

%d bloggers like this: