问题
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
回答1:
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.
来源:https://stackoverflow.com/questions/33285103/capturing-a-lambdas-static-in-a-nested-lambda