Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is it not possible to use private method in a lambda?

Tags:

Having a class like this:

class A {
public:
    bool hasGrandChild() const;

private:
    bool hasChild() const;
    vector<A> children_;
};

Why is it not possible to use a private method hasChild() in a lambda expression defined in the method hasGrandChild() like this?

bool A::hasGrandChild() const {
    return any_of(children_.begin(), children_.end(), [](A const &a) {
        return a.hasChild();
    });
}

Compiler issues an error that the method hasChild() is private within the context. Is there any workaround?

Edit: It seems that the code as I posted it originally works. I thought that it is equivalent, but the code that does not work on GCC is more like this:

#include <vector>
#include <algorithm>

class Foo;

class BaseA {
protected:
    bool hasChild() const { return !children_.empty(); }
    std::vector<Foo> children_;
};

class BaseB {
protected:
    bool hasChild() const { return false; }
};

class Foo : public BaseA, public BaseB {
public:
  bool hasGrandChild() const {
    return std::any_of(children_.begin(), children_.end(), [](Foo const &foo) {
        return foo.BaseA::hasChild();
      });
  }  
};

int main()
{
  Foo foo;
  foo.hasGrandChild();
  return 0;
}

Seems that there is a problem with fully qualified names as this does not work, but this works.

like image 325
Juraj Blaho Avatar asked Aug 13 '12 12:08

Juraj Blaho


People also ask

Can we use private for methods?

Yes, we can have private methods or private static methods in an interface in Java 9. We can use these methods to remove the code redundancy. Private methods can be useful or accessible only within that interface only. We can't access or inherit private methods from one interface to another interface or class.

Why would you use a private method?

Private methods are useful for breaking tasks up into smaller parts, or for preventing duplication of code which is needed often by other methods in a class, but should not be called outside of that class.


2 Answers

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.

like image 57
Juraj Blaho Avatar answered Oct 01 '22 04:10

Juraj Blaho


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:

  1. Write a local class type that explicitly captures this along with any other locals it requires (inspired by Björn Pollex's comment below).
  2. Write a 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.
like image 44
Jon Avatar answered Oct 01 '22 03:10

Jon