2012-08-13 5 views
19

eine Klasse wie folgt zu haben:Warum ist es nicht möglich, private Methode in einem Lambda zu verwenden?

class A { 
public: 
    bool hasGrandChild() const; 

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

Warum ist es nicht möglich, eine private Methode hasChild() in einem Lambda-Ausdruck in dem Verfahren hasGrandChild() wie folgt definiert zu benutzen?

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

Compiler eine Fehlermeldung, dass das Verfahren hasChild() im Rahmen privat ist. Gibt es eine Problemumgehung?

Edit: Es scheint, dass der Code, wie ich es ursprünglich geschrieben funktioniert. Ich dachte, dass es äquivalent ist, aber der Code, die does not work on GCC mehr ist wie folgt:

#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; 
} 

scheint, dass es ein Problem mit voll qualifizierten Namen als this does not work, aber this works.

+1

Der Verschluss hat keine Beziehung zu Ihrer Klasse 'A', so natürlich nicht darauf zugreifen können' A' nicht-öffentlichen Mitglieder. Es kann es auch nie, da sein Typname nicht zu erkennen ist, so dass man es nicht einmal zum "Freund" machen kann. –

+2

Ist es nur ich oder funktioniert das auf gcc? http://ideone.com/333qw – pmr

+0

@pmr: Ja, es scheint in älteren GCC zu funktionieren, aber funktioniert nicht in neueren. –

Antwort

27

Es scheint nur ein GCC Fehler in einem speziellen Fall zu sein, wenn das Lambda ein geschütztes Mitglied von Klasse zuzugreifen versucht, Eltern voll qualifizierte Namen. 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(); 
    }); 
    } 
}; 

, aber this works:

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

Nach 11 C++, 5.1.2/3:

Der Typ des lambda-Ausdruck (der auch Der Typ des -Abschlussobjekts) ist ein eindeutiger unbenannter Nicht-Unionsklassen-Typ, der Verschlusstyp genannt wird und dessen Eigenschaften im Folgenden beschrieben werden. Diese Klassenart ist kein Aggregat (8.5.1). Der Schließungstyp wird im kleinsten Blockbereich, Klassenbereich oder Namespacebereich deklariert, der den entsprechenden Lambda-Ausdruck enthält.

Und dann 11 C++, 11,7/1:

Eine verschachtelte Klasse ist ein Mitglied und hat als solche die gleichen Zugriffsrechte wie ein anderes Mitglied.

Also sollte das erwähnte funktionslokale Lambda die gleichen Zugriffsrechte haben wie jedes andere Mitglied der Klasse. Daher sollte es in der Lage sein, eine geschützte Methode von einer Elternklasse aufzurufen.

+1

Große Erklärung, warum es * sollte * funktionieren. (Und warum die meisten anderen Antworten hier falsch sind.) +1 – etherice

0

Es ist nicht möglich, weil das Lambda nicht Teil der Klasse ist. Es ist das Gleiche wie eine Out-of-Class-Funktion zu erstellen und sie aufzurufen, anstatt ein Lambda zu erstellen. Natürlich hat es keinen Zugang zu privaten Mitgliedern.

9

der Standard (C++ 11, §5.1.2/3) besagt, dass

Der Typ des lambda-Ausdruck (der auch der Typ des Verschluss Objekt ist) ist ein einzigartiges, unbenannter Nicht-Vereinigungsklassen-Typ - der Verschlusstyp.

Da es ein einzigartiger Klasse-Typ, der keine friend von A ist, es hat keinen Zugang zu A ‚s privaten Mitgliedern.

Was der Compiler hier tut, ist einen Klassentyp zu erstellen, der über geeignete Member verfügt, um alle erfassten Variablen zu speichern, also operator() usw. - was genau Sie selbst schreiben würden, wenn Sie Lambda in C++ 03 emulieren wollten. Dieser Typ hätte sicherlich keinen Zugriff auf Member private, die es leichter veranschaulichen können, warum die Einschränkung existiert und warum gibt es keine Problemumgehung .

aktualisieren in Bezug auf mögliche Abhilfen:

Es wäre besser, zu sagen: „Es gibt keine Abhilfen einen Lambda mit“, weil im allgemeinen Abhilfen existieren, obwohl sie verlangen, dass Sie die bequeme Lambda-Syntax verzichten. Zum Beispiel könnten Sie:

  1. eine lokale Klasse Art schreiben, die es ausdrücklich verlangt this zusammen mit anderen Einheimischen einfängt (inspiriert von Björn Pollex Kommentar unten).
  2. Schreiben Sie eine private Methode anstelle eines Lambda und übergeben Sie diese als Callback (z. B. mit std::bind für die Bequemlichkeit). Wenn Sie zusätzlich zu this Ortsansässige erfassen möchten, können Sie dazu auf der Call-Site mehr std::bind verwenden.
+2

Es funktioniert mit einem lokalen 'struct' obwohl (http://ideone.com/AvsBE). Kannst du den Unterschied erklären? –

+0

@ BjörnPollex: Das ist ein * local * 'struct', deshalb hat es Zugriff auf die privaten Mitglieder seines enthaltenden Typs (zitiert das Buch: * Die lokale Klasse ist im Umfang des umschließenden Bereichs und hat die gleiche Zugriff auf Namen außerhalb der Funktion sowie die umschließende Funktion. *). Man könnte sagen, dass es sich um eine funktionale Problemumgehung handelt. – Jon

+0

@Jon: Das erklärt nicht, warum der Compiler den Schließtyp nicht einfach zu einem Freund der Klasse macht, zu der er gehört.Wenn es "dieses" implizit erfassen kann und somit effektiv wie eine Mitgliedsfunktion funktioniert, sollte es in der Lage sein, andere private Mitglieder zu erreichen. –

3

Umgehung:

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

Warten. Warum zum Teufel würdest du nicht 'any_of (beginnen, enden, std :: mem_fn (& A :: hasChild))' als? Neben der geringfügigen Leistungsstrafe. – pmr

+0

@pmr - Dies ist hauptsächlich zu veranschaulichen, wie auf private Member-Funktionen in Lambda-Ausdrücken zugreifen. – Henrik

+0

@pmr: Leistungsstrafe? Ich würde nicht einen erwarten; Für einen vernünftigen Compiler ist es offensichtlich, dass sich 'f' nicht ändert. – MSalters

3

Sie können this explizit erfassen und es zu einem "Member Lambda" machen, das Zugriff auf private Mitglieder hat.

Betrachten wir zum Beispiel die folgende Beispiel:

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