2013-03-27 7 views
12

Ho questo codice:È sicuro lanciare una funzione lambda su un puntatore di funzione?

void foo(void (*bar)()) { 
    bar(); 
} 

int main() { 
    foo([] { 
     int x = 2; 
    }); 
} 

Tuttavia, io sono preoccupato che questo subirà la stessa sorte:

struct X { int i; }; 

void foo(X* x) { 
    x->i = 2; 
} 

int main() { 
    foo(&X()); 
} 

che prende l'indirizzo di una variabile locale.

Il primo esempio è completamente sicuro?

risposta

20

Una lambda che cattura nulla è implicitamente convertibile in un puntatore a funzione con la stessa lista di argomenti e tipo di ritorno. Solo i lambda senza cattura possono farlo; se cattura qualcosa, allora non possono.

A meno che non si stia utilizzando VS2010, che non ha implementato quella parte dello standard, poiché non esisteva ancora quando stavano scrivendo il compilatore.

5

Oltre alla risposta generale perfettamente corretto Nicol s', vorrei aggiungere alcuni punti di vista sui vostri particolari timori:

Tuttavia, io sono preoccupato che questo subirà la stessa sorte ... , che prende l'indirizzo di una variabile locale.

Certo che lo è, ma questo è assolutamente alcun problema quando basta chiamare dentro foo (nello stesso modo in cui il vostro esempio struct è perfettamente funzionante), dal momento che la funzione circostante (main in questo caso) che ha definito il la variabile locale/lambda sopravviverà comunque alla funzione chiamata (foo). Potrebbe sempre essere un problema se si volesse proteggere quella variabile locale o il puntatore lambda per un uso successivo. Quindi

Il primo esempio è completamente sicuro?

Sì, lo è, come anche il secondo esempio.

+3

"* Sì, lo è, come anche il secondo esempio. *" Pedanticamente parlando, il secondo esempio è illegale poiché prende l'indirizzo di un temporaneo, quindi non è né sicuro né pericoloso. ; -] – ildjarn

+0

@ildjarn Hah, non se ne rendeva nemmeno conto, io idiota. Quindi cosa fare ora, tecnicamente l'intera risposta potrebbe essere spazzatura, poiché l'esempio lambda prende anche l'indirizzo di un temporaneo. Ma poi di nuovo è un lambda e la funzione stessa dovrebbe praticamente * "essere lì" * tutto il tempo. Forse è il momento di cogliere lo standard per quella che sembra essere una domanda piuttosto facile. –

+0

L'esempio lambda non prende l'indirizzo di un temporaneo, converte semplicemente un oggetto lambda in un puntatore di funzione. Ad ogni modo, IMHO il tuo sarcasmo è fuori luogo. – enobayram

4

Sì. Credo che il primo esempio sia sicuro, indipendentemente dal tempo di vita di tutti i temporari creati durante la valutazione dell'espressione completa che coinvolge l'espressione lambda senza cattura.

Per la bozza di lavoro (n3485) 5.1.2 [expr.prim.lambda] p6

Il tipo di chiusura per una lambda espressione senza lambda-acquisizione ha un non pubblico non virtuale funzione di conversione const esplicita al puntatore per funzionare con gli stessi parametri e tipi di ritorno della chiusura operatore di chiamata di funzione del tipo . Il valore restituito da questa funzione di conversione deve essere l'indirizzo di una funzione che, una volta invocato, ha lo lo stesso effetto del richiamo dell'operatore di chiamata di funzione del tipo di chiusura.

Il paragrafo precedente non dice nulla sulla validità del pointer-to-function che scade dopo la valutazione dell'espressione lambda.

Ad es., Mi aspetterei quanto segue per lavorare:

auto L = []() { 
    return [](int x, int y) { return x + y; }; 
}; 

int foo(int (*sum)(int, int)) { return sum(3, 4); } 


int main() { 
    foo(L()); 
} 

Mentre i dettagli di implementazione di clang non sono certo l'ultima parola su C++ (lo standard è), se ti fa sentire meglio, il modo in cui questo viene implementato in clang è che quando l'espressione lambda viene analizzata e analizzata semanticamente viene inventato un tipo di chiusura per l'espressione lambda e una funzione statica viene aggiunta alla classe con semantica simile all'operatore di chiamata di funzione del lambda. Quindi, anche se il tempo di vita dell'oggetto lambda restituito da 'L()' è finito nel corpo di 'foo', la conversione in pointer-to-function restituisce l'indirizzo di una funzione statica che è ancora valida.

Si consideri il caso in qualche modo analogo:

struct B { 
    static int f(int, int) { return 0; } 
    typedef int (*fp_t)(int, int); 
    operator fp_t() const { return &f; } 
}; 
int main() { 
    int (*fp)(int, int) = B{}; 
    fp(3, 4); // You would expect this to be ok. 
} 

io non sono certamente un esperto di core-C++, ma FWIW, questa è la mia interpretazione della lettera della norma, e sento che è difendibile.

Spero che questo aiuti.

+0

+1 per "indipendentemente dal tempo di vita". Dopo tutto, l'oggetto lambda temporaneo creato viene convertito in un puntatore a funzione, il suo indirizzo non viene utilizzato. – enobayram

Problemi correlati