2012-03-12 12 views
6

È possibile limitare il tipo di cattura di un lambda dato come parametro?
Ad esempio, è possibile prendere solo lambda che non catturano nulla per riferimento?Vincoli di cattura lambda C++

template <typename F> 
void f(const F& lambda) // F must be a lambda that do not capture by ref 
{ 
    ::: 
} 
+2

Quando l'oggetto di chiusura raggiunge la tua funzione, è già stato creato e non hai idea di cosa ci sia dentro, come se fossero private. Ma nel caso ve lo stiate chiedendo, il lambda certamente non può catturare nulla * dopo il fatto *, e in particolare niente dall'interno della * vostra * funzione 'f'. –

+5

Questo sarebbe inutile, poiché catturare un puntatore per valore è pericoloso quanto catturare un riferimento. –

+0

@ BenVoigt Questo è giusto. – log0

risposta

5

MSalters nota che "le lambda non di cattura possono essere convertite in un puntatore a funzione". Cosa significa questo? L'oggetto lambda corrisponderà a un puntatore al tipo di parametro della funzione.

È difficile tradurre il tipo di lambda in un puntatore-a-funzione. Ecco il mio tentativo di implementazione conforme. È leggermente hackerato.

#include <type_traits> 

template< typename fn > 
struct ptmf_to_pf; 

template< typename r, typename c, typename ... a > 
struct ptmf_to_pf< r (c::*) (a ...) const > 
    { typedef r (* type)(a ...); }; 

// Use SFINAE to hide function if lambda is not convertible to function ptr. 
// Only check that the conversion is legal, it never actually occurs. 

template< typename lambda > 
typename std::enable_if< std::is_constructible< 
     typename ptmf_to_pf< decltype(&lambda::operator()) >::type, 
     lambda >::value >::type 
f(lambda arg) { 
    arg("hello "); 
    arg("world\n"); 
} 

#include <iostream> 

int main() { 
int x = 3; 
    f([](char const *s){ std::cout << s; }); // OK 
    f([=](char const *s){ std::cout << s; }); // OK 
    f([=](char const *s){ std::cout << s << x; }); // error 
} 

Questo non accetterà i puntatori di funzione come argomenti diretti, poiché il parametro modello deve essere risolto in un funtore. Puoi farlo facendo una specializzazione per ptmf_to_pf che accetta il puntatore ai tipi di funzione.

Inoltre, come dimostra la demo, non accetterà lambda che catturano nulla in base al valore, oltre che per riferimento. Non c'è modo in C++ di rendere la restrizione così specifica.

+0

Perché non passare semplicemente lambda come puntatore a funzione, quindi, senza modelli? – perreal

+1

@perreal: se conosci il tipo di puntatore a funzione desiderato, fallo. Nel caso generale questo non è noto. – Potatoswatter

5

forse stai incomprensione comportamento cattura di espressioni lambda: Un oggetto di chiusura è proprio come un oggetto funtore precisato, in modo

struct Fun 
{ 
    Fun (int & a) : n(a) { } 
    int operator()(...) { ... } 
private: 
    int & n; 
}; 

int q; 
Fun f(q); 
f(...); 

è esattamente lo stesso di

int q; 
auto f = [&q](...) -> int { ... }; 
f(...); 

Una volta costruito l'oggetto di chiusura, tutta la cattura e il legame sono finiti e bloccati per sempre nell'oggetto.

Se ora si passa l'oggetto in un altro posto, ad esempio call_me(f), la funzione destinatario non ha alcuna connessione con la costruzione del funtore o dell'oggetto di chiusura.

+1

È vero, ma il tipo generato dal compilatore di lambda contiene tutti i dettagli di come si è verificata l'acquisizione. Si potrebbe facilmente immaginare un sistema di metadati che consentisse l'introspezione di quel tipo per vedere se ci fossero membri di riferimento (o se il costruttore avesse dei parametri di riferimento). –

+0

@BenVoigt sì che potrebbe essere un tratto dell'oggetto functor ... – log0

3

Hack indiretto: solo le lambda non acquisibili possono essere convertite in un puntatore a funzione. Naturalmente, questo copre anche molti tipi di F che non sono affatto lambda.

+0

Permettere puntatori di funzioni non lambda è probabilmente innocuo o vantaggioso.+1 – Potatoswatter

Problemi correlati