2014-10-31 10 views
8

mi aveva intenzione di scrivere un modello di memorizzazione in C++ e si è conclusa con il seguente approccioLambda cattura da parte delle forze di valore tutte ambito oggetto da 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; 
    } 

Stranamente, il mio compilatore VS 2012, ha rifiutato di compilare con il seguente errore

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) 

Mi sembra che il compilatore catturi deliberatamente tutto in base al valore come oggetto const. Non riesco a trovare alcun riferimento documentato a questo comportamento.

Qualcuno può aiutarmi a capire cosa sta succedendo qui?

+0

Non dovrebbe essere "memo.count (pos)'? Allo stato attuale, per qualsiasi valore diverso da 0, calcoliamo e memorizziamo sempre il valore, anche se esiste già. –

+0

@PhilWright: Sì, vero. In realtà stavo cercando di eseguire il debug del mio codice e durante la pubblicazione ho dimenticato di cambiarlo. Grazie per averlo evidenziato. – Abhijit

risposta

14

Lambdade si comportano più o meno come oggetti funzione; come un oggetto funzione hanno un operatore di chiamata di funzione, ovvero operator(). Per non mutable lambda, questa funzione è const:

[expr.prim.lambda]

5 Il tipo di chiusura per un non-generico lambda-espressione ha un pubblico linea operatore chiamata di funzione [. ..] Questa funzione operatore di call o modello di operatore è dichiarata const (9.3.1) se e solo se il parametro-dichiarazione-clausola della del lambda-espressione è non seguita da mutable.

Perché entità catturate dalla copia si comportano come se fossero variabili membro della lambda:

15 [...] Per ogni entità catturato da copia, è dichiarato un membro di dati non statico senza nome nel tipo di chiusura.

e non mutable membri non possono essere modificati all'interno di una funzione const membro ([class.this]/1, [dcl.type.cv]/4), se si desidera modificare le entità catturate si dovrà dichiarare un lambda mutable.

Così com'è il tuo lambda assomiglia a questo:

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

Si può pensare a un mutable lambda come avere un non constoperator(), nel tuo caso il lambda può essere definito come segue:

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

+1, spiegazione perfetta relativa a lambda con funzioni oggetto (funtori). – vsoftco

6

al fine di rendere una funzione lambda non-const è necessario aggiungere la parola chiave mutable:

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; 
    } 
Problemi correlati