31

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).

1 Answer 1

35

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.

Sign up to request clarification or add additional context in comments.

4 Comments

This seems to contradict this answer, which says if it's ODR unused then it's not actually captured.
@GManNickG: The difference is that of implicit capturing ([=]) and explicit capturing ([someNameHere]). Implicit capturing requires ODR used elements. Explicit capturing does not.
@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.
FWIW expr.prim.lambda.closure/2 says this: An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing the size and/or alignment [..] Not sure whether this allows eliding the capture-by-copy but I suppose not, as copy-initialization of the captured variable has observable side effect to the abstract machine.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.