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