29

I have a decent amount of code that relies on capturing a shared_from_this() when using a lambda expression as a callback to ensure that my instance stays alive:

std::shared_ptr<Thing> self = shared_from_this();
auto doSomething = [this, self] ()
{
    // various statements, none of which reference self, but do use this
}

So the question is: Since I am not referencing self inside the lambda body, is a conformant compiler allowed to optimize the capture away?


Consider the following program:

#include <functional>
#include <iostream>
#include <memory>

std::function<void ()> gFunc;

struct S : std::enable_shared_from_this<S>
{
    void putGlobal()
    {
        auto self = shared_from_this();
        gFunc = [self] { };
    }
};

int main()
{
    auto x = std::make_shared<S>();
    std::cout << x.use_count() << std::endl;
    x->putGlobal();
    std::cout << x.use_count() << std::endl;
}

The output is:

1
2

This indicates that g++-4.7.1 does not optimize the capture away (nor does clang-3.1).

Travis Gockel
  • 26,877
  • 14
  • 89
  • 116

1 Answers1

34

The standard guarantees that captured values are not optimized away (per §5.1.2/14):

An entity is captured by copy if it is implicitly captured and the capture-default is = or if it is explicitly captured with a capture that does not include an &. For each entity captured by copy, an unnamed non- static data member is declared in the closure type. The declaration order of these members is unspecified. The type of such a data member is the type of the corresponding captured entity if the entity is not a reference to an object, or the referenced type otherwise.

So, self is copied into the closure on evaluation (per §5.1.2/21):

When the lambda-expression is evaluated, the entities that are captured by copy are used to direct-initialize each corresponding non-static data member of the resulting closure object.

GManNickG
  • 494,350
  • 52
  • 494
  • 543
Travis Gockel
  • 26,877
  • 14
  • 89
  • 116
  • 1
    This seems to contradict [this answer](http://stackoverflow.com/questions/6181464/c11-lambda-capture-semantics), which says if it's ODR unused then it's not actually captured. – GManNickG Oct 03 '12 at 23:53
  • 11
    @GManNickG: The difference is that of implicit capturing (`[=]`) and *explicit* capturing (`[someNameHere]`). Implicit capturing requires ODR used elements. Explicit capturing does not. – Nicol Bolas Oct 04 '12 at 00:06
  • @NicolBolas: Gotcha. An entity is implicitly captured if and only if it's ODR used. At that point, it's in the same boat as explicitly captured entities. Thanks. – GManNickG Oct 04 '12 at 00:09