2016-06-15 9 views
10

Come mostrato in questa pagina http://en.cppreference.com/w/cpp/thread/async, uno della firma del std::async in C++ 14 è stato modificato dal C++ 11 versionetipi di decadimento prima di passare a std :: result_of

template< class Function, class... Args> 
std::future<typename std::result_of<Function(Args...)>::type> 
    async(Function&& f, Args&&... args); 

a

template< class Function, class... Args> 
std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>> 
    async(Function&& f, Args&&... args); 

le modifiche sono le std::decay_t s (che rimuovono riferimenti e CV-qualificazioni e array decadimento/funzioni in puntatori) applicati su tipi di funzioni e argomenti prima che siano passati al std::result_of. Non riesco a capire perché il decadimento sia utile. Ad esempio, per un tipo di funzione Fn (forse un alias di tipo di una classe di chiusura), passando Fn, Fn&&, const Fn& ecc. Sembrano tutti produrre lo stesso risultato.

Qualcuno può darmi un esempio concreto in cui il decadimento è utile?

UPDATE: A titolo di esempio, questo codice:

#include <iostream> 
#include <type_traits> 

int main() 
{ 
    auto fn = [](auto x) -> int { return x + 1; }; 

    using Fn = decltype(fn); 
    using FnRef = Fn&; 
    using FnCRef = const Fn&; 
    using FnRRef = Fn&&; 

    std::cout << std::boolalpha 
       << std::is_same<int, std::result_of_t<Fn(int)>>::value << '\n' 
       << std::is_same<int, std::result_of_t<FnRef(int)>>::value << '\n' 
       << std::is_same<int, std::result_of_t<FnCRef(int)>>::value << '\n' 
       << std::is_same<int, std::result_of_t<FnRRef(int)>>::value << '\n'; 

    return 0; 
} 

sarà stampare quattro true s.

+0

Penso che abbia un significato per riferimento a un tipo di funzione. – 101010

+0

@ 101010 Non 'decay' rimuove la qualifica di riferimento? – NathanOliver

+0

@ 101010 Si prega di consultare il mio aggiornamento. –

risposta

8

La modifica è in risposta a LWG 2021. Il problema è che il decadimento di async (come bind, ecc.) Copia tutti i suoi argomenti e quindi se non hai usato decay nel tipo di ritorno avresti ottenuto il tipo di ritorno sbagliato quando si tratta di qualifiche di ref e/o valore -ness:

struct F { 
    int operator()() &; 
    char operator()() &&; 

    int operator(int&) const; 
    char operator(int&&) const; 
}; 

auto future = std::async(F{}); // actually gives future<int>, but says 
           // it gives future<char>? 
auto future2 = std::async(F{}, 1); // ditto 

dal momento che tutti gli argomenti sono asincrone MoveConstructed nel suo oggetto interno, sarà necessario avvolgerli abilmente al fine di raggiungere in realtà rvalue-ness degli argomenti.

Questo ha senso - asyncdeve archiviare i suoi argomenti da qualche parte, e se si passa in rvalues ​​deve assumerne la proprietà. Se mantiene i riferimenti di valore, l'oggetto sottostante potrebbe essere distrutto. Ma una volta memorizzato come T, non sa da dove proviene un T& o un T&& - ha solo un argomento lvalue con nome in quel punto.

+0

Ah. Cose come queste sono le ragioni per cui adoriamo il C++. –

+2

Si noti che la nuova versione implica che 'sposta' la funzione e gli argomenti fuori dalla sua memoria interna nell'espressione di chiamata, che è anche la cosa giusta da fare. – Yakk

+0

@Yakk La diversa semantica di 'bind()' lo rende piuttosto confuso. Mi rendo conto che thread/async sono call-one e bind è potenzialmente call-many, ma è ancora ... confusionario. – Barry