2014-11-05 4 views
10

Ho notato che lambdas funzionano entrambi utilizzando i puntatori di funzione e il tipo dedicato function utilizzando g++.Quale modo di invocare lambda in C++ è il più idiomatico?

#include <functional> 

typedef int(*fptr)(int, int); 

int apply_p(fptr f, int a, int b) { 
    return f(a, b); 
} 

int apply_f(std::function<int(int, int)> f, int a, int b) { 
    return f(a, b); 
} 

void example() { 
    auto add = [](int a, int b) -> int { 
    return a + b; 
    }; 

    apply_p(add, 2, 3); // doesn't give an error like I'd expect it to 
    apply_f(add, 2, 3); 
} 

La mia domanda è: quali di questi sono più idiomatici da usare? E quali sono i pericoli e/oi benefici dell'usare l'uno sull'altro?

+0

Correlato a [questa domanda] (http://stackoverflow.com/q/26748736/1708801) chiesto oggi.La conversione al puntatore funzione funziona solo se lambda non ha acquisito. Come risposta c'è un'altra alternativa che usa un modello e tratta il lambda come un Callable. 'std :: function' incorre in overhead e non può essere ottimizzato come una lambda can. –

+1

La seconda versione potrebbe comportare un sovraccarico non necessario. Vorrei invece applicare 'apply_f' a un template di funzione. – juanchopanza

+0

@ShafikYaghmour wow .. tempismo casuale –

risposta

10

Ho notato che lambda sia il lavoro usando puntatori a funzione così come il function tipo dedicato

Se il lambda non catturare qualsiasi cosa, allora può decadere ad un puntatore a funzione.

Quali di questi sono i più idiomatici da usare?

Nessuno dei due. Utilizzare function se si desidera archiviare un oggetto callable arbitrario. Se si desidera solo per creare e utilizzare una sola, tenerlo generico:

template <typename Function> 
int apply(Function && f, int a, int b) { 
    return f(a,b); 
} 

Si può andare oltre e rendere il tipo di ritorno e l'argomento (s) generici; Lo lascerò come esercizio.

E quali sono i pericoli e/o i vantaggi dell'utilizzo degli uni sugli altri?

La versione del puntatore a funzione funziona solo con funzioni (non membro o statiche) e lambda non acquisiti e non consente il passaggio di nessuno stato; solo la funzione stessa e i suoi argomenti. Il tipo function può includere qualsiasi tipo di oggetto chiamabile, con o senza stato, quindi è generalmente più utile. Tuttavia, questo ha un costo in fase di esecuzione: per nascondere il tipo spostato, chiamarlo coinvolgerà una chiamata di funzione virtuale (o simile); e potrebbe aver bisogno dell'allocazione dinamica della memoria per contenere un tipo grande.

+0

non è pericoloso tenerlo _that_ generico? non perde completamente ogni tipo di sicurezza facendo questo? –

+3

@ElectricCoffee: Non vedo alcun pericolo e certamente non perde la sicurezza di tipo * all *. Permette qualsiasi tipo per cui 'f (a, b)' è un'espressione ben formata convertibile in 'int' da usare. Altri tipi saranno rifiutati. –

4

direi (C++ 14)

template <class Functor, class... Args> 
decltype(auto) apply_functor(Functor&& f, Args&&... args) { 
    return std::forward<Functor>(f)(std::forward<Args>(args)...); 
} 

o C++ 11:

template <class Functor, class... Args> 
auto apply_functor(Functor&& f, Args&&... args) ->decltype(std::forward<Functor>(f)(std::forward<Args>(args)...)) { 
    return std::forward<Functor>(f)(std::forward<Args>(args)...); 
} 
+0

Puoi prendere in considerazione la possibilità di renderlo 'std :: forward (f) (std :: forward (args) ...);' per preservare il categoria di valore dell'argomento 'f'. – Niall

+0

@Niall grazie, risolto. Anche cambiato 'auto' in' decltype (auto) 'per la versione C++ 14 per supportare il ritorno per riferimento. –

2

In caso di dubbio, preferiscono std::function:

  1. La sintassi è molto più semplice e coerente con altri tipi, si consideri ad es

    typedef std::function<int (int, int)> MyAddFunction; 
    

    e

    typedef int (*MyAddFunction)(int, int); 
    
  2. È più generico (in realtà può prendere un lambda o una funzione C normale o un funtore)

  3. È più sicuro tipo

    apply_p(0, 0, 0); // probably crashes at runtime, dereferences null pointer 
    apply_f(0, 0, 0); // doesn't even compile 
    
+1

Cosa sono 'apply_f' e' apply_p'? –

+1

@ AdriC.S. Le funzioni definite nella domanda. –

+0

@MikeSeymour Sì, ma intendevo quale: è 'apply_f' è la funzione e 'apply_p' è il puntatore? –

Problemi correlati