2013-05-21 31 views
6

consideri il seguente esempio stupido:Setter di una funzione lambda?

class MyClass 
{ 
    public: 
     template <class Function> 
     inline double f(double x, Function&& function) 
     { 
      return function(x); 
     } 
}; 

Con questa classe, posso chiamare MyClass::f(x, function), con una funzione di lambda per eseguirlo su x, con (spero) non appesantisce. La mia domanda è: quale sarebbe l'equivalente con lo function come membro configurabile dello MyClass?

class MyClass 
{ 
    public: 
     inline double f(double x) 
     { 
      return _function(x); 
     } 
    // What are the setter and the type of the protected member _function ? 
}; 
+2

potrebbe essere qualsiasi cosa che prende un singolo doppio e restituisce un double. Potresti usare 'std :: function '. – juanchopanza

risposta

10

Le funzioni lambda (così come altri tipi di funzioni "callable") possono essere spostate e memorizzate utilizzando la classe di modello std::function, trovata nell'intestazione <functional>. Il suo parametro di modello è una firma di funzione con la sintassi

ReturnType(ArgumentType1, ArgumentType2, ...) 

Quindi nel tuo caso tutto il tipo di funzione wrapper diventa

std::function<double(double)> 

e, quindi, il codice diventa

class MyClass 
{ 
    public: 
     inline double f(double x) 
     { 
      return _function(x); 
     } 
     void setFunction(std::function<double(double)> && f) 
     { 
      _function = f; 
     } 
    private: 
     std::function<double(double)> _function; 
}; 

std::function è " più "di un wrapper per i puntatori di funzione. Come saprai, le funzioni lambda possono acquisire parte del contesto delle variabili, che deve essere memorizzato da qualche parte. std::function fa questo per te in modo trasparente.

Nota che std::function non supporta le firme sovraccaricate/operatori di chiamata basati su modelli per i funtori. Quando si assegna un funtore con una firma dell'operatore di chiamata come T operator()(T value) a std::function<double(double)>, può essere chiamato solo con questa firma. Quindi non esiste lo std::function<T(T)> (a meno che non sia noto lo T, ad esempio un parametro del modello della classe).


Un'alternativa che potrebbe essere più efficiente, in alcuni casi (è necessario punto di riferimento/profilo it), è quello di rendere tutta la classe una classe template con il parametro tipo di funzione è il parametro di template.Poi si può memorizzare una funzione come membro:

template<typename Function> 
class MyClass 
{ 
    public: 
     MyClass(Function && f) : 
      _function(f) 
     {} 
     inline double f(double x) 
     { 
      return _function(x); 
     } 
    private: 
     Function _function; 
}; 

Al fine di creare un oggetto del genere, è necessario specificare il parametro del modello, in questo modo:

auto myLambda = [](double x){ return x * 0.25; }; 
MyClass<decltype(myLambda)> myObject { myLambda }; 

Per evitare questo overhead sintattica brutto, aggiungere una funzione "maker" che sfrutta tipo di modello deduzione:

template<typename Function> 
auto makeMyClass(Function && f) -> MyClass<Function> { 
    return MyClass<Function>(f); 
} 

Poi, il codice diventa più leggibile, con l'utilizzo di auto ancora:

auto myLambda = [](double x){ return x * 0.25; }; 
auto myObject = makeMyClass(myLambda); 
+2

Mi piace il tuo metodo di classe basato su modelli. Analizzerò tutto ciò e pubblicherò i risultati più tardi. – Vincent

+0

Un rapido punto di riferimento di 1 miliardo di chiamate di una funzione di base che richiede 3 doppie e restituisce un doppio (g ++ -O3) mi dice che non vi è alcuna differenza pratica tra una chiamata diretta e il mio esempio "stupido" all'inizio. Con un membro della classe che usa std :: function o il tuo trucco di classe basato su modelli, c'è un sovraccarico di circa il 15% di tempo. – Vincent

3

Si dovrà utilizzare un std::function involucro, possibilmente lasciando che il costruttore inizializza da un ingresso oggetto invocabile:

#include <functional> // <== REQUIRED FOR std::function 

class MyClass 
{ 
    public: 

     template<typename F> 
     MyClass(F&& f) : _function(std::forward<F>(f)) { } 

     inline double f(double x) 
     { 
      return _function(x); 
     } 

    private: 

     std::function<double(double)> _function;  
}; 

avviso, che questo può portare un po 'di run-time in testa, ma il il design è semplice e flessibile. A meno che non sia possibile dimostrare che questo sovraccarico di esecuzione è un collo di bottiglia per i requisiti di prestazioni del software, questo overhead di runtime non dovrebbe costituire un problema.

+0

Dovrò fare un benchmark, perché questo è per calcoli intensivi con probabilmente centinaia di milioni di chiamate. – Vincent

+0

@Vincent: In effetti, in questo caso dovresti valutarlo. Se ciò che state passando è un puntatore a funzione o un lambda non acquisibile (che può essere convertito in un puntatore a funzione) e l'implementazione è sufficientemente intelligente, è probabile che il sovraccarico sia minimo. Ma bene, quando si tratta di prestazioni, non ci si deve mai fidare di supposizioni che non sono supportate da misurazioni concrete. –

+0

@Vincent Ho aggiunto una soluzione alternativa alla mia risposta, potresti voler controllare. – leemes

Problemi correlati