Re: lambda : capturing a data member - is it legal?
Johannes Schaub (litb) wrote:
Helmut Jarausch wrote:
Hi,
reading Scott Meyers' "Overview of the New C++" I stumbled over
#include <algorithm>
#include <list>
class Widget {
public:
void doSomething();
private:
std::list<int> li;
int minVal;
};
void Widget::doSomething() {
auto it = std::find_if(li.cbegin(), li.cend(),
[minVal](int i) { return i > minVal; } // error?
);
}
Scott says, that the second to last line is in error and 'repairs' it by
capturing 'this' instead of minVal.
On the other hand, gcc-4.5.2 accepts the code above.
[snipped]
BUT I think that the wording of N3225 renders your code ill-formed. It
requires that any identifier in the capture-clause names an automatic
variable declared in the reaching scope of the lambda. The reaching scope
only covers up to and including the inner-most enclosing function. The
class scope of that function, if the function is a class member, is
lexically outside that scope. Class members also doesn't necessarily have
automatic storage duration. So the wording of n3225 seems to forbid
capturing class members, but I don't think that's the intent (If I'm not
missing something).
Correction: I do think now that the spec forbids capturing non-static class
members by value. I think it's not an accident that the "reachin scope"
of a
lambda expression does not enclose the class scope, but that another thing
was in error. Following is my explanation of why I thought that N3225 is
not
stating its intent correctly. Previously I was confused by the text in the
spec that reads:
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;
If you read this according to what it exactly says, then the following
implicitly captures "a" once and once not:
struct A { int a; void f(); };
void A::f() {
[=] { return a; };
}
A b;
void f() {
A a;
a.f(); // now it captures
b.f(); // now it does not!
}
And it will be ill-formed, because the text continues with
; such entities shall be declared within the reaching scope of
the lambda expression.
Therefor the program would be ill-formed in the first evaluation. In the
first one, it's not captured, but that's fine. The spec only requires used
automatic variables from the reaching scope to be captured.
However in the second evaluation, it's captured but not declared within the
reaching scope, thus it's an error. I don't believe it's possible to
implement these semantics sanely, as they depend on where the object was
created for which a function is called. In the presence of member-pointers,
the diagnostic must thus be a runtime diagnostic, which is a bit weird.
The error in the spec is that it tries to implicitly capture non-static
data
members that have automatic storage duration. It should not do so, but
restrict the implicit capture to local variables with automatic storage
duration or to straight out only restrict the implicit capture to automatic
variables declared within the reaching scope. The use of other automatic
variables would be ill-formed by other paragraphs of the spec.
In the end, in the above snippets, it odr-uses "this" and "a", but "a" is
not seen as a variable with automatic storage duration by the lambda-spec
(which is an issue, since it *is* such a variable) and thus it only
captures
"this" (and this is the intent, which I confirmed by having a private
discussion with Daniel. Thanks Daniel!). The access to "a" is still by-ref.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]