In this answer I use this code:
std::vector<std::vector<int>> imat(3, std::vector<int>(10));
std::for_each(imat.begin(), imat.end(), [&](auto& i) {
static auto row = 0;
auto column = 0;
std::transform(i.begin(), i.end(), i.begin(),
[&](const auto& /*j*/) {
return row * column++;
});
++row;
});
But I notice some misbehavior in capturing static auto row
depending upon the compiler.
Clang 3.7.0 yields:
0 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9
0 2 4 6 8 10 12 14 16 18
gcc 5.1.0 yields:
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
And Visual Studio 2015 gives me a compile time error:
An internal error has occurred in the compiler.
If I change the capture nested capture to capture row
explicitly I get the compiler error:
identifier in capture must be a variable with automatic storage duration declared in the reaching scope of the lambda
Am I allowed to capture a static
in a nested lambda? It seems legit, but there are so many problems!
EDIT:
Fozi pointed out that I can get Visual Studio 2015 to compile and give the same output as Clang 3.7.0 if I change the nested lambda's parameter type from const auto&
to const int&
. Which seems completely unrelated, but it works.
This doesn't work if I try to capture row
explicitly. In that case I still get the compiler error:
identifier in capture must be a variable with automatic storage duration declared in the reaching scope of the lambda
I've reported a Visual Studio 2015 bug here: https://connect.microsoft.com/VisualStudio/feedback/details/1930409/capturing-a-lambdas-static-in-a-nested-lambda
Much like functions can change the value of arguments passed by reference, we can also capture variables by reference to allow our lambda to affect the value of the argument. To capture a variable by reference, we prepend an ampersand ( & ) to the variable name in the capture.
Capture clause A lambda can introduce new variables in its body (in C++14), and it can also access, or capture, variables from the surrounding scope. A lambda begins with the capture clause. It specifies which variables are captured, and whether the capture is by value or by reference.
A lambda object must not outlive any of its reference captured objects. Lambda expressions may capture objects with automatic storage duration from the set of enclosing scopes (called the reaching scope) for use in the lambda's function body.
A lambda or anonymous method may have a static modifier. The static modifier indicates that the lambda or anonymous method is a static anonymous function. A static anonymous function cannot capture state from the enclosing scope.
An Internal Compiler Error(ICE) is always a bug.
We don't need to capture variables of static storage duration but we do need to capture automatic variables that are odr-used. From the draft C++ standard section 5.1.2
:
The lambda-expression’s compound-statement yields the function-body (8.4) of the function call operator, but for purposes of name lookup (3.4), determining the type and value of this (9.3.2) and transforming idexpressions referring to non-static class members into class member access expressions using (*this) (9.3.1), the compound-statement is considered in the context of the lambda-expression.
so row
should be visible within the inner lambda and:
[...]If a lambda-expression or an instantiation of the function call operator template of a generic lambda odr-uses (3.2) this or a variable with automatic storage duration from its reaching scope, that entity shall be captured by the lambda-expression.[...]
Capture is only required for this
and variables of automatic storage duration if they are odr-used and we can see that explicit capture is only defined for automatic variables or this:
The identifier in a simple-capture is looked up using the usual rules for unqualified name lookup (3.4.1); each such lookup shall find an entity. An entity that is designated by a simple-capture is said to be explicitly captured, and shall be this or a variable with automatic storage duration declared in the reaching scope of the local lambda expression.
For both Visual Studio and gcc to match the results of clang I can move row
out to the global namespace, see it live for gcc. Also as Fozi points out changing const auto& /*j*/
to const int& /*j*/
makes it start working.
It looks like gcc accepts explicit capture of non-automatic variables as an extension and even then explicitly capturing row
for example [&, &row](const auto & )
still produces all zeros.
Further for gcc if I move the definition for row
to main
then I see the following error (see it live):
/tmp/cchzwtQI.s: Assembler messages:
/tmp/cchzwtQI.s:1572: Error: symbol `_ZL3row' is already defined
Which seems like a compiler error to me.
I don't see any portion of the standard that would make the original program ill-formed. Nor should changing the auto
to int
make a difference and non of the changes introduced by polymorphic lambda proposal would seem to explain this difference either.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With