Having a class like this:
class A {
public:
bool hasGrandChild() const;
private:
bool hasChild() const;
vector children_;
};
You can capture this
explicitly and make it a "member lambda" that has access to private members.
For example, consider the following sample:
#include <iostream>
class A {
private:
void f() { std::cout << "Private"; }
public:
void g() {
[this] {
f();
// doesn't need qualification
}();
}
};
class B {
private:
void f() { std::cout << "Private"; }
public:
void g() { [] { f(); }(); } // compiler error
};
int main() {
A a;
a.g();
}
It seems to be just a GCC bug in a special case when the lambda tries to access a protected member from parent class using fully qualified name. This does not work:
class Base {
protected:
bool hasChild() const { return !childs_.empty(); }
std::vector<Foo> childs_;
};
class Foo : public Base {
public:
bool hasGrandChild() const {
return std::any_of(childs_.begin(), childs_.end(), [](Foo const &foo) {
return foo.Base::hasChild();
});
}
};
, but this works:
class Foo : public Base {
public:
bool hasGrandChild() const {
return std::any_of(childs_.begin(), childs_.end(), [](Foo const &foo) {
return foo.hasChild();
});
}
};
According to C++11, 5.1.2/3:
The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type — called the closure type — whose properties are described below. This class type is not an aggregate (8.5.1). The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression.
And then C++11, 11.7/1:
A nested class is a member and as such has the same access rights as any other member.
So the mentioned function-local lambda should have the same access rights as any other member of the class. Therefore it should be able to call a protected method from a parent class.
The standard (C++11, §5.1.2/3) states that
The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type — called the closure type.
Since it's a unique class type that is not a friend
of A
, it doesn't have access to A
's private members.
What the compiler does here is create a class type that has appropriate members to store any captured variables, an appropriate operator()
etc -- which is exactly what you would write yourself if you wanted to emulate lambdas in C++03. This type would certainly not have access to private
members, which might make it easier to visualize why the limitation exists and why there is no workaround.
Update regarding possible workarounds:
It would be better to say "there are no workarounds using a lambda", because in general workarounds do exist although they require that you forgo the convenient lambda syntax. For example, you could:
this
along with any other locals it requires (inspired by Björn Pollex's comment below).private
method instead of a lambda and pass that as the callback (e.g. using std::bind
for convenience). If you want to capture locals in addition to this
you can use more std::bind
at the call site to do so.It isn't possible because the lambda is not a part of the class. It's the same as making an out-of-class function, and calling it instead of creating a lambda. Of course it doesn't have access to private members.
Workaround:
typedef bool (A::*MemFn)(void) const;
bool A::hasGrandChild() const {
MemFn f = &A::hasChild;
return any_of(childs_.begin(), childs_.end(), [=](A const &a) {
return (a.*f)();
});
}