Pourquoi n’est-il pas possible d’utiliser une méthode privée dans un lambda?

Avoir un cours comme celui-ci:

class A { public: bool hasGrandChild() const; private: bool hasChild() const; vector children_; }; 

Pourquoi n’est-il pas possible d’utiliser une méthode privée hasChild() dans une expression lambda définie dans la méthode hasGrandChild() comme ceci?

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

Le compilateur hasChild() une erreur indiquant que la méthode hasChild() est privée dans le contexte. Y at-il une solution de contournement?

Edit: Il semble que le code tel que je l’ai posté fonctionne à l’origine. Je pensais que c’était équivalent, mais le code qui ne fonctionne pas sur GCC ressemble plus à ceci:

 #include  #include  class Foo; class BaseA { protected: bool hasChild() const { return !children_.empty(); } std::vector 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; } 

Il semble qu’il y ait un problème avec les noms entièrement qualifiés car cela ne fonctionne pas , mais cela fonctionne .

Il semble s’agir simplement d’un bogue GCC dans un cas particulier où le lambda tente d’accéder à un membre protégé de la classe parent à l’aide d’un nom complet. Cela ne marche pas :

 class Base { protected: bool hasChild() const { return !childs_.empty(); } std::vector childs_; }; class Foo : public Base { public: bool hasGrandChild() const { return std::any_of(childs_.begin(), childs_.end(), [](Foo const &foo) { return foo.Base::hasChild(); }); } }; 

, mais cela fonctionne :

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

Selon C ++ 11, 5.1.2 / 3:

Le type de l’expression-lambda (qui est également le type de l’object de fermeture) est un type de classe unique, sans nom et sans union, appelé le type de fermeture, dont les propriétés sont décrites ci-dessous. Ce type de classe n’est pas un agrégat (8.5.1). Le type de fermeture est déclaré dans la plus petite scope de bloc, de classe ou d’espace de nom qui contient l’expression lambda correspondante .

Et puis C ++ 11, 11.7 / 1:

Une classe nestede est un membre et a donc les mêmes droits d’access que n’importe quel autre membre.

Donc, la fonction locale lambda mentionnée devrait avoir les mêmes droits d’access que n’importe quel autre membre de la classe. Par conséquent, il devrait pouvoir appeler une méthode protégée depuis une classe parente.

La norme (C ++ 11, §5.1.2 / 3) stipule que

Le type de l’expression-lambda (qui est également le type de l’object fermeture) est un type de classe unique, non nommé et non syndiqué, appelé type de fermeture .

Comme il s’agit d’un type de classe unique qui n’est pas un friend de A , il n’a pas access aux membres privés de A

Le compilateur crée un type de classe avec les membres appropriés pour stocker toutes les variables capturées, un operator() approprié operator() etc., qui correspond exactement à ce que vous écririez vous-même si vous vouliez émuler lambdas en C ++ 03. Ce type n’aurait certainement pas access à private membres private , ce qui pourrait permettre de visualiser plus facilement pourquoi la limitation existe et pourquoi il n’y a pas de solution de contournement .

Mise à jour concernant les solutions de contournement possibles:

Il serait préférable de dire “il n’y a pas de solution de contournement utilisant un lambda”, car en général, les solutions de contournement existent, bien qu’elles exigent que vous renonciez à la syntaxe pratique lambda. Par exemple, vous pourriez:

  1. Ecrivez un type de classe local qui capture explicitement this ainsi que tous les autres locaux dont il a besoin (inspiré par le commentaire de Björn Pollex ci-dessous).
  2. Ecrivez une méthode private place d’un lambda et transmettez-la comme rappel (par exemple, utilisez std::bind pour plus de commodité). Si vous voulez capturer des locaux en plus de this vous pouvez utiliser plus de std::bind sur le site d’appels pour le faire.

Solution de contournement:

 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)(); }); } 

Vous pouvez capturer this explicitement et en faire un “membre lambda” qui a access aux membres privés.

Par exemple, considérons l’exemple suivant:

 #include  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; ag(); } 

Ce n’est pas possible car le lambda ne fait pas partie de la classe. Cela revient à faire une fonction hors classe et à l’appeler au lieu de créer un lambda. Bien sûr, il n’a pas access aux membres privés.