2012-02-08 10 views
40

Vorrei creare una classe in cui il client può memorizzare un'espressione lambda come []() -> void {} come campo della classe, ma non riesco a capire come farlo. One answer suggested using decltype, che ho provato senza successo. Ecco uno ideone source link. Il sotto è la fonte e il risultato:Come posso memorizzare un'espressione lambda come campo di una classe in C++ 11?

#include <cstdio> 
auto voidLambda = []()->void{}; 

class MyClass { 
public: 
    decltype(voidLambda) t; 
    MyClass(decltype(voidLambda) t) { 
     this->t = t; 
    } 
}; 

int main() { 
    MyClass([] { 
     printf("hi"); 
    }); 
} 

Risultato:

prog.cpp: In constructor 'MyClass::MyClass(<lambda()>)': 
prog.cpp:3:79: error: no matching function for call to '<lambda()>::__lambda0()' 
prog.cpp:2:20: note: candidates are: <lambda()>::<lambda>(const<lambda()>&) 
prog.cpp:2:20: note:     <lambda()>::<lambda>(<lambda()>&&) 
prog.cpp:3:88: error: no match for 'operator=' in '((MyClass*)this)->MyClass::t = t' 
prog.cpp: In function 'int main()': 
prog.cpp:5:27: error: no matching function for call to 'MyClass::MyClass(main()::<lambda()>)' 
prog.cpp:3:48: note: candidates are: MyClass::MyClass(<lambda()>) 
prog.cpp:3:14: note:     MyClass::MyClass(const MyClass&) 

Qualcuno sa come fare questo?

+5

Ogni lambda crea un proprio tipo unico. In 'auto A = []() {}; auto B = []() {}; '' A' e 'B' non sono dello stesso tipo. – bames53

+1

Sfortunatamente 'struct A {auto x = 0; }; 'non è permesso. –

risposta

40

Se si desidera che un membro della classe sia un'espressione lambda, considerare l'utilizzo del tipo di wrapper std::function<> (dall'intestazione <functional>), che può contenere qualsiasi funzione chiamabile. Ad esempio:

std::function<int()> myFunction = []() { return 0; } 
myFunction(); // Returns 0; 

In questo modo, non è necessario conoscere il tipo di espressione lambda. Puoi semplicemente memorizzare un std::function<> del tipo di funzione appropriato e il sistema di template gestirà tutti i tipi per te. Più in generale, qualsiasi entità richiamabile della firma appropriata può essere assegnata a un std::function<>, anche se il tipo effettivo di quel functor è anonimo (nel caso di lambda) o molto complicato.

Il tipo all'interno del modello std::function deve essere il tipo di funzione corrispondente alla funzione che si desidera memorizzare. Quindi, ad esempio, per memorizzare una funzione che occupa due int s e restituisce nulla, devi creare un valore std::function<void (int, int)>. Per una funzione che non accetta parametri e restituisce un valore int, verrà utilizzato std::function<int()>. Nel tuo caso, dal momento che si desidera una funzione che non accetta parametri e restituisce void, che ci si vuole qualcosa di simile:

class MyClass { 
public: 
    std::function<void()> function; 
    MyClass(std::function<void()> f) : function(f) { 
     // Handled in initializer list 
    } 
}; 

int main() { 
    MyClass([] { 
     printf("hi") 
    }) mc; // Should be just fine. 
} 

Spero che questo aiuti!

+0

Domanda: è '-> void' davvero necessario qui? So che la clausola di restituzione può essere omessa (e quindi dedotta) in un certo numero di circostanze e questo mi sembra iniziare come un caso facile (no 'return'). –

+0

@ MatthieuM.- In realtà non sono sicuro! Avevo l'impressione che dovessi averlo, ma se mi sbaglio sarei felice di aggiornare questa risposta. – templatetypedef

+0

Beh, sto imparando anch'io, quindi speravo * tu * lo sapessi :) –

7

L'unico modo che posso pensare di memorizzare un lambda in una classe è quello di utilizzare un modello con una funzione di supporto make_:

espressione
#include <cstdio> 
#include <utility> 

template<class Lambda> 
class MyClass { 
    Lambda _t; 
public: 
    MyClass(Lambda &&t) : _t(std::forward<Lambda>(t)) { 
     _t(); 
    } 
}; 

template<class Lambda> 
MyClass<Lambda> make_myclass(Lambda &&t) { 
    return { std::forward<Lambda>(t) }; 
} 

int main() { 
    make_myclass([] { 
     printf("hi"); 
    }); 
} 
+4

aggira la necessità di specificare argomenti del template introducendo questo helper 'make_myclass'. Questo è l'idioma [Object generator] (https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Object_Generator). – mucaho

Problemi correlati