2015-10-18 21 views
5

Sto creando una coda di lavoro. Il lavoro verrà creato nel thread A, quindi il lavoro verrà inviato al thread B e il thread B svolgerà il lavoro. Dopo che il lavoro è stato fatto, il lavoro verrà inviato al filo A.Troppe copie durante il binding di argomenti modello variad.

#include <functional> 
#include <iostream> 
#include <memory> 

using namespace std; 

template<typename T, typename... Args> 
class Job 
{ 
    public: 
     Job(std::weak_ptr<T> &&wp, std::function<void(const Args&...)> &&cb) 
      : _cb(std::move(cb)), 
       _cbWithArgs(), 
       _owner(std::move(wp)) {} 

    public: 
     template<typename... RfTs> 
     void bind(RfTs&&... args) 
     { 
      // bind will copy args for three times. 
      _cbWithArgs = std::bind(_cb, std::forward<RfTs>(args)...); 
     } 

     void fire() 
     { 
      auto sp = _owner.lock(); 
      if (sp) 
      { 
       _cbWithArgs(); 
      } 
     } 

    private: 
     std::function<void(const Args& ...)> _cb; 
     std::function<void()> _cbWithArgs; 
     std::weak_ptr<T> _owner; 
}; 

struct Args 
{ 
    Args() = default; 
    Args(const Args &args) 
    { 
     cout << "Copied" << endl; 
    } 
}; 

struct Foo 
{ 
    void show(const Args &) 
    { 
     cout << "Foo" << endl; 
    } 
}; 

int main() 
{ 
    using namespace std::placeholders; 

    shared_ptr<Foo> sf (new Foo()); 
    Args args; 

    // Let's say here thread A created the job. 
    Job<Foo, Args> job(sf, std::bind(&Foo::show, sf.get(), _1)); 

    // Here thread B has finished the job and bind the result to the 
    // job. 
    job.bind(args); 

    // Here, thread A will check the result. 
    job.fire(); 
} 

I codici di cui sopra si compila e opere. Ma dà i seguenti risultati (g ++ 4.8.4 & clang hanno gli stessi risultati):

Copied 
Copied 
Copied 
Foo 

ci sono tre copie! Non accettabile, non so dove ho sbagliato. Perché tre copie? Ho cercato su Google e trovato un metodo da qui: https://stackoverflow.com/a/16868401, copia solo i parametri per una volta. Ma deve inizializzare la funzione bind nel costruttore.

Grazie, Piotr Skotnicki. Sfortunatamente, non ho un compilatore C++ 14. Quindi, facciamo i Args mobile:

struct Args 
{ 
    Args() = default; 
    Args(const Args &args) 
    { 
     cout << "Copied" << endl; 
    } 
    Args(Args &&) = default; 
    Args& operator=(Args &&) = default; 
}; 

Ora, copys solo una volta :)

Infine, ho adottato i codici da questa discussione https://stackoverflow.com/a/16868151/5459549. Il modello gen_seq è vero ART, devo dire.

risposta

5

La prima copia è fatta dal std::bind stessa:

std::bind(_cb, std::forward<RfTs>(args)...) 

quanto deve memorizzare cariati-copie dei suoi argomenti.

Le altre due copie sono realizzate con la cessione _cbWithArgs che è di tipo std::function (§ 20.8.11.2.1 [func.wrap.func.con]):

template<class F> function& operator=(F&& f); 

Effetti:function(std::forward<F>(f)).swap(*this);

dove:

template<class F> function(F f); 

[...] *this obiettivi una copia di f inizializzato con std::move(f).

Cioè, una copia per il parametro di un costruttore e un'altra per archiviare l'argomento f.

Da Args è un tipo non mobile, un tentativo di passaggio da risultato di una chiamata a std::bind ricade a una copia.

Non sembra ragionevole nel codice memorizzare i risultati dell'esecuzione del lavoro in un std::function.Invece, è possibile memorizzare i valori in una tupla:

template <typename T, typename... Args> 
class Job 
{ 
public: 
    Job(std::weak_ptr<T> wp, std::function<void(const Args&...)> &&cb) 
     : _cb(std::move(cb)), 
      _owner(wp) {} 

public: 
    template<typename... RfTs> 
    void bind(RfTs&&... args) 
    { 
     _args = std::forward_as_tuple(std::forward<RfTs>(args)...); 
    } 

    void fire() 
    { 
     auto sp = _owner.lock(); 
     if (sp) 
     { 
      apply(std::index_sequence_for<Args...>{}); 
     } 
    } 

private:  
    template <std::size_t... Is> 
    void apply(std::index_sequence<Is...>) 
    { 
     _cb(std::get<Is>(_args)...); 
    } 

    std::function<void(const Args&...)> _cb; 
    std::weak_ptr<T> _owner; 
    std::tuple<Args...> _args; 
}; 

DEMO

+0

Purtroppo non viene compilato in VC14, std :: tuple deve inizializzare – MORTAL

+0

@MORTAL * "Purtroppo non si compila in VC14, std :: la tupla deve essere inizializzata "*, puoi elaborare? –

+0

grazie per la risposta informativa, ho eseguito il codice in VC14 non compilato, restituisce errore come 'tuple (67): errore C2476: il costruttore 'constexpr' non inizializza tutti i membri'. non so se questo potrebbe essere un altro bug con VC14 – MORTAL

Problemi correlati