2013-08-21 10 views
14

Ho notato che è impossibile passare un riferimento non const come argomento a std::async.Il passaggio degli argomenti a std :: async per riferimento non riesce

#include <functional> 
#include <future> 

void foo(int& value) {} 

int main() { 
    int value = 23; 
    std::async(foo, value); 
} 

mio compilatore (GCC 4.8.1) dà il seguente errore per questo esempio:

error: no type named ‘type’ in ‘class std::result_of<void (*(int))(int&)>’ 

Ma se io avvolgere il valore passato a std::async in std::reference_wrapper, tutto è OK. Presumo che ciò sia dovuto al fatto che lo std::async prende gli argomenti in base al valore, ma non riesco ancora a capire il motivo dell'errore.

+3

Non è necessario digitare 'std :: reference_wrapper'. Usa 'std :: ref'. – chris

+0

Io uso 'std :: ref', ma grazie per un suggerimento :) – lizarisk

+0

Dovrai scavare nel codice sorgente di libstdC++ se ti interessa il motivo di questo errore: http: //gcc.gnu .org/onlinedocs/gcc-4.8.1/libstdC++/api/a01256_source.html I numeri di riga da esaminare sono negli errori. In bocca al lupo. – jrok

risposta

24

È una scelta progettuale/trade-off deliberata.

In primo luogo, non è necessariamente possibile scoprire se il functionoid passato a async prende i suoi argomenti per riferimento oppure no. (Se non si tratta di una funzione semplice ma di un oggetto funzione, ad esempio potrebbe avere un operatore di chiamata con funzione sovraccaricata. Quindi, async non può dire, "Ehi, lasciami controllare cosa vuole la funzione obiettivo, e farò il giusto cosa."

Quindi la domanda di progettazione è, prende tutti gli argomenti per riferimento se possibile (vale a dire se sono lvalue), o fa sempre delle copie? Fare copie è la scelta sicura qui: una copia non può diventare penzoloni e una copia non può esibire condizioni di gara (a meno che non sia davvero strano). Quindi questa è la scelta che è stata fatta: tutti gli argomenti sono copiati di default.

Tuttavia, il meccanismo è stato scritto in modo tale che in realtà non riesce a passare gli argomenti a un parametro di riferimento non costante. Questa è un'altra scelta per la sicurezza: altrimenti, la funzione che ti aspetteresti di modificare il tuo lvalue originale modifica invece la copia, portando a bachi molto difficili da rintracciare.

Ma cosa succede se si vuole davvero il parametro di riferimento non costante? Cosa succede se prometti di prestare attenzione a riferimenti e condizioni di gara penzoloni? Questo è ciò che std :: ref è per. È un esplicito opt-in per la pericolosa semantica di riferimento. È il tuo modo di dire: "So cosa sto facendo qui".

15

std::async (e altre funzioni che eseguono l'inoltro perfetto) guarda il tipo di argomento che passi per capire cosa fare. Non guardano come verrà utilizzata questa discussione. Quindi, per passare un oggetto per riferimento, devi dire allo std::async che stai usando un riferimento. Tuttavia, il semplice fatto di passare un riferimento non lo farà. È necessario utilizzare std::ref(value) per passare value come riferimento.

+0

Risposta molto chiara. – Sheen

14

Il problema si è solo marginalmente legato alla std::async(): Quando si definisce il risultato dell'operazione, std::async() utilizza std::result_of<...>::type con tutti i suoi argomenti essendo std::decay<...>::type 'ed. Questo è ragionevole perché std::async() accetta tipi arbitrari e li inoltra per archiviarli in qualche luogo. Per memorizzarli, i valori sono necessari per l'oggetto funzione e per gli argomenti. Così, std::result_of<...> viene utilizzato simile a questo:

typedef std::result_of<void (*(int))(int&)>::type result_type; 

... e dato int non può essere vincolata ad un int& (int non è un tipo Ivalue stato è necessario per essere vincolato a int&), questo fallisce . In questo caso, l'errore indica che std::result_of<...> non definisce un nidificato type.

Una domanda di follow-up potrebbe essere: che tipo è utilizzato per l'istanziazione di std::result_of<...>? L'idea è che la sintassi della chiamata di funzione consistente in ResultType(ArgumentTypes...) sia abusata: invece di un tipo di risultato, viene passato un tipo di funzione e std::result_of<...> determina il tipo della funzione chiamata quando viene chiamato quel tipo di funzione con la lista di argomenti specificata. Per i tipi di puntatori a funzione non è davvero così interessante, ma il tipo di funzione può anche essere un oggetto funzione in cui è necessario prendere in considerazione l'overloading. Quindi, in pratica, std::result_of<...> viene utilizzato in questo modo:

typedef void (*function_type)(int&); 
typedef std::result_of<function_type(int)>::type result_type; // fails 
typedef std::result_of<function_type(std::reference_wrapper<int>)>::type result_type; //OK 
+1

+1 IMHO questo post è quello che * veramente * analizza il messaggio di errore mostrato nell'OP. –

Problemi correlati