Lifetime of lambda captures

2019-03-30 15:30发布

Given the following program:

#include <iostream>
#include <memory>
using namespace std;
int main() {
    std::shared_ptr<int> i(new int(42));
    cout << i.use_count() << endl;
    auto fn = [=](){i; cout << 42 << endl;};
    cout << i.use_count() << endl;
    return 0;
}

When does the compiler decide which objects it will capture?
The shared_ptr i is never used in the lambda expression. So in a normal function I would assume that the optimizer will remove this nop statement.
But if it is removed the compiler could think that i needs not to be captured.

So with gcc this program will always produce 1,2 as an output.
But is this guaranteed by the standard?

标签: c++ lambda
2条回答
不美不萌又怎样
2楼-- · 2019-03-30 16:27

With a default-capture of [=], any local variable is captured if it's odr-used within the lambda. The definition of odr-used is:

A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression and the lvalue-to-rvalue conversion is immediately applied.

Here, i is an evaluated expression, and isn't a constant; so it is odr-used and therefore is captured; whether or not evaluating the expression has any effect.

But is this guaranteed by the standard?

tl;dr yes.

查看更多
趁早两清
3楼-- · 2019-03-30 16:31

If we go to cppreference page on lambda function they have the following explanation:

[=] captures all automatic variables mentioned in the body of the lambda by value

and further says:

The capture-list is a comma-separated list of zero or more captures, optionally beginning with the capture-default. The only capture defaults are & (implicitly catch the odr-used automatic variables and this by reference) and = (implicitly catch the odr-used automatic variables and this by value).

The reference section for odr-used says:

a variable is odr-used if its name appears as a potentially-evaluated expression, except if all of the following is true:

  • applying lvalue-to-rvalue conversion to the exression yields a constant expression that doesn't invoke non-trivial functions
  • the expression is either discarded-value expression or an lvalue-to-rvalue conversion

The exceptions do not apply to i so i will be captured.

Which agrees with the draft C++11 standard section 5.1.2 Lambda expressions paragraph 11 which says:

If a lambda-expression has an associated capture-default and its compound-statement odr-uses (3.2) this or a variable with automatic storage duration and the odr-used entity is not explicitly captured, then the odr-used entity is said to be implicitly captured; such entities shall be declared within the reaching scope of the lambda expression.

查看更多
登录 后发表回答