2015-08-26 14 views
8

In this talk by Sutter a 1:15:26 è stato presentato un codice come qui di seguito,Perché questo codice è veloce per char *?

class employee{ 
std::string name_; 

public: 
template<class String, class= 
std::enable_if_t< !std::is_same<std::decay_t<String>, std::string>::value > > 
void set_name(String && name) 
    noexcept(std::isnothrow_assignable<std::string &, String>::value) 
    { 
    name_ = std::forward<String>(name); 
    } 
} 

So come std::forward opere, se name è un lvalue, avranno copia costruiti name_; e se name è un valore, name_ verrà spostato costruito. Ma nella diapositiva si dice anche che Optimized to steal from rvalues (and more), cosa c'è di più?

E in seguito mostra che questo codice sembra il più veloce tra tutte e quattro le implementazioni, in particolare per char *, chiunque ha la pazienza di leggere questo codice e spiegare cosa viene ottimizzato di più e perché è il più veloce, in particolare nel caso di char *?

+0

Nota le funzioni a cui sta confrontando prendono un 'std :: string' in una forma o nell'altra. Se il tuo obiettivo era solo scrivere un codice che fosse veloce per 'char *' non devi _avere_ per usare l'inoltro perfetto. –

risposta

10

Prima di tutto, si noti che il codice contiene un errore di battitura e che il vincolo enable_if non esegue ciò che è stato discusso anche con l'errore di battitura rimosso; In particolare, la funzione non funzionerà con char*, quindi ovviamente non è la più veloce con char*. Puoi vedere una domanda che ho posto su questo here insieme a un setter di inoltro perfetto 'corretto' (insieme al supporto di questa versione di Howard Hinnant).

template <class String> 
auto set_name(String&& name) 
-> decltype(name_ = std::forward<String>(name), void()) { 
    name_ = std::forward<String>(name); 
} 

Il riferimento che viene utilizzato per la presentazione chiama set_name ripetutamente su un employee per mostrare il caso in cui l'elemento string ottiene riutilizzare sua capacità invece di esserci un'allocazione memoria ogni iterazione. La differenza tra le barre alte e le barre corte mostrate nei benchmark è la differenza tra riutilizzare la capacità e fare un'allocazione ogni iterazione.

La ragione spedizioniere perfetta corretta è veloce con char* perché l'istanza del modello per char* solo inoltra un char* invece di dover costruire un param string per chiamare set_name tramite un parametro effettivo string oggetto. L'assegnazione all'interno del setter è veloce perchéimplementa operator=(char*) che evita la memcpy efficiente nello storage esistente e le allocazioni extra.

Quindi la richiesta Optimized to steal from rvalues (and more) è perché l'inoltro perfetto non fa altro che inoltro. Non solo passa attraverso un rvalue se ottiene un rvalue, ma non converte anche i tipi, il che significa che anche le ottimizzazioni implementate come "forwardee" comeoperator=(char*) vengono esposte.

+0

Vedo. Upvote per "Non solo passa attraverso un rvalue se ottiene un rvalue, ma anche non convertiti in tipi non necessari". – Allanqunzi

+0

il tuo 'set_name()' non restituisce nulla, e ha un tipo di ritorno –

+1

@ BЈовић: Il tipo di ritorno (dopo SFINAE) è 'decltype (void())' così 'void'. – Jarod42

Problemi correlati