2015-09-09 23 views
15

This answer spiega come spostare-catturare una variabile all'interno di una lambda in C++ 14.Spostamento di una lambda: una volta spostato, acquisito un tipo di solo spostamento, come può essere utilizzato il lambda?

Ma dopo aver spostato un oggetto non copiabile (come ad esempio std::unique_ptr) all'interno di una lambda, non è possibile copiare la lambda stessa.

Questo andrebbe bene se si potesse spostare lambda, ma ottengo un errore di compilazione quando si cerca di farlo:

using namespace std; 

class HasCallback 
{ 
    public: 
    void setCallback(std::function<void(void)>&& f) 
    { 
     callback = move(f); 
    } 

    std::function<void(void)> callback; 
}; 

int main() 
{ 
    auto uniq = make_unique<std::string>("Blah blah blah"); 
    HasCallback hc; 
    hc.setCallback(
     [uniq = move(uniq)](void) 
     { 
     std::cout << *uniq << std::endl; 
     }); 

    hc.callback(); 
} 

Questo produce il seguente errore con g++ (ho tentato di copiare solo la linea pertinente):

error: use of deleted function ‘main()::<lambda()>::<lambda>(const main()::<lambda()>&’ 

... insinuando, penso, che il mio tentativo di spostare il lambda ha fallito.

clang++ restituisce un errore simile.

Ho provato esplicitamente move in lambda (anche se è un valore temporaneo), ma ciò non ha aiutato.

MODIFICA: Le risposte seguenti rispondono in modo adeguato agli errori di compilazione prodotti dal codice precedente. Per un approccio alternativo, è sufficiente release il valore di destinazione del puntatore univoco in un std::shared_ptr, che può essere copiato da. (Non sto scrivendo questo come una risposta, perché ciò presuppone che si tratti di un problema XY, ma il motivo sottostante per cui unique_ptr non può essere utilizzato in un lambda che viene convertito in un std::function è importante da comprendere.)

EDIT 2: In modo abbastanza strano, ho appena realizzato auto_ptr farebbe davvero la cosa giusta qui (!), Per quanto posso dire. Agisce essenzialmente come unique_ptr, ma consente la costruzione della copia al posto della costruzione del movimento.

+0

penso setCallback dovrebbe ottenere parametro per valore piuttosto che di riferimento rvalue, mi sbaglio? – Slava

+0

@Slava Questo è ciò che avevo originariamente, ma ha dato lo stesso errore. Pensavo che prendere il riferimento di valore avrebbe permesso (/ forzare) il lambda di essere costruito con il movimento, ma non sembra essere il caso. –

risposta

14

È possibile spostare lambda, va bene. Tuttavia, non è questo il tuo problema, stai cercando di creare un'istanza di std::function con un lambda non personalizzabile. E il: costruttore

template< class F > 
function(F f); 

di function fa:

5) inizializza il bersaglio con un copia di f.

Questo perché std::function:

soddisfano i requisiti della CopyConstructible e CopyAssignable.

Poiché function deve essere copiato, tutto ciò che viene inserito deve essere anche copiabile. E un lambda con solo movimento non soddisfa questo requisito.

+0

.... eh. Grazie-- Stavo attraversando un momento molto difficile per analizzare questi messaggi di errore anche per questo semplice caso. C'è un modo per cambiare la firma per funzionare, a meno di usare un parametro di modello con qualifica universal-ref anziché 'std :: function'? –

+0

@KyleStrand Non importa quale sia il parametro, non è possibile costruire la 'funzione'. Se hai bisogno di qualcosa di tipo cancellato, dovresti scrivere un equivalente 'function' mobile. – Barry

+0

@Barry ci sono solo le alternative std :: functions già disponibili solo per citarne due: https://github.com/Naios/Function2 e https://github.com/potswa/cxx_function –

10

std::function non è un lambda!È un involucro che può essere costruito da qualsiasi tipo di callable, incluso un lambda. std::function richiede che lo callable be copy-constructible, che è il motivo per cui l'esempio non riesce.

Un lambda di solo spostamento può essere spostato nuovamente come mostrato di seguito.

template<typename F> 
void call(F&& f) 
{ 
    auto f1 = std::forward<F>(f); // construct a local copy 
    f1(); 
} 

int main() 
{ 
    auto uniq = make_unique<std::string>("Blah blah blah"); 
    auto lambda = [uniq = move(uniq)]() { 
     std::cout << *uniq << std::endl; 
     }; 
// call(lambda); // doesn't compile because the lambda cannot be copied 
    call(std::move(lambda)); 
} 

Live demo

+0

Ack. Sapevo che 'std :: function' non è un lambda, ma dal momento che lambda è convertibile in' std :: function' non ho mantenuto la distinzione chiara nella mia mente. –

+0

Quindi, come implementare HasCallback nel codice originale? Quello che intendo è, come salvare il lambda di solo spostamento in un contenitore di muovere solo o spostare solo la struttura di dati? – alpha

+0

@alpha Dovresti o usare il lambda direttamente senza convertirlo in un altro tipo (cioè la funzione che chiama un lambda dovrebbe prendere il lambda come argomento di tipo template), o usare una classe di funzione diversa (ci sono varie alternative a 'std :: function' in the wild, oppure potresti crearne di tue). –

Problemi correlati