2014-10-31 13 views
8

ich beabsichtigte, ein Auswendiglernen Muster in C++ zu schreiben und endete mit folgendem AnsatzLambda Erfassung von Wert alle Kräfte Objekt scoped const

std::function<int(int)> Memoize(std::function<int(int)> fn) 
    { 
     std::map<int, int> memo; 
     std::function<int(int)> helper = [=](int pos) 
     { 
      if (memo.count(pos) == 0) 
      { 
       memo[pos] = fn(pos); 
      } 
      return memo[pos]; 
     }; 
     return helper; 
    } 

Seltsam, mein Compiler VS 2012, weigerte sich mit der folgenden kompilieren Fehler

1>Source1.cpp(24): error C2678: binary '[' : no operator found which takes a left-hand operand of type 'const std::map<_Kty,_Ty>' (or there is no acceptable conversion) 

Es scheint mir, dass der Compiler absichtlich alles als const Objekt wert erfasst. Ich kann keinen dokumentierten Hinweis auf dieses Verhalten finden.

Kann mir jemand helfen zu verstehen, was hier möglicherweise passiert?

+0

Sollte es nicht 'memo.count (pos)' sein? Für andere Werte als 0 berechnen und speichern wir den Wert immer, auch wenn er bereits existiert. –

+0

@PhilWright: Ja, stimmt. Eigentlich habe ich versucht, meinen Code zu debuggen und während des Postings vergessen zu ändern. Danke für die Hervorhebung. – Abhijit

Antwort

14

Lambdas verhalten sich mehr oder weniger wie Funktionsobjekte; wie ein Funktionsobjekt haben sie einen Funktionsaufrufoperator, d. h. operator(). Für nicht mutable lambdas ist diese Funktion const:

[expr.prim.lambda]

5 Der Verschlusstyp für einen nicht-generic Lambda-Ausdruck hat einen öffentlichen Inline-Funktionsaufruf Operator [. ..] Diese Funktion Call-Betreiber oder Operator Vorlage wird const erklärt (9.3.1), wenn und nur wenn der Parameter-Deklaration-Klausel des Lambda-Ausdrucks ist nicht von mutable gefolgt.

Da durch Kopie erfasst Einheiten verhalten sich, als wären sie Membervariablen des Lambda:

15 [...] Für jede von Kopie erfasst Einheit, ein ungenannter nicht-statisches Datenelement deklariert im Verschlusstyp.

und Nicht-Mitglieder mutablekönnen nicht innerhalb einerconst Elementfunktion modifiziert werden ([class.this]/1, [dcl.type.cv]/4), wenn Sie möchten, die erfassten Einheiten modifizieren Sie muss ein mutable Lambda deklarieren.

Wie es Ihr Lambda steht wie folgt aussieht:

class Helper 
{ 
public: 
    int operator()(int) const; 
private: 
    std::map<int, int> memo; 
    std::function<int(int)> fn; 
}; 

Sie ein mutable Lambda denken können, eine nicht constoperator() hat, kann das Lambda in Ihrem Fall wie folgt definiert werden:

std::function<int(int)> helper = [=](int pos) mutable 
// etc 
+1

+1, perfekte Erklärung in Bezug auf Lambdas mit Objektfunktionen (Funktoren). – vsoftco

6

um eine Lambda-Funktion nicht-const Sie das mutable Schlüsselwort hinzufügen müssen zu machen:

std::function<int(int)> Memoize(std::function<int(int)> fn) 
    { 
     std::map<int, int> memo; 
     std::function<int(int)> helper = [=](int pos) mutable // <== HERE!! 
     { 
      if (memo.count(0) == 0) 
      { 
       memo[pos] = fn(pos); 
      } 
      return memo[pos]; 
     }; 
     return helper; 
    }