2011-12-23 17 views
32

Eventuali duplicati:
Move semantics == custom swap function obsolete?Sta specializzando std :: swap deprecato ora che abbiamo spostato la semantica?

Questo è il modo std::swap si presenta come in C++ 11:

template<typename T> 
void swap(T& x, T& y) 
{ 
    T z = std::move(x); 
    x = std::move(y); 
    y = std::move(z); 
} 

Devo ancora specializzarsi std::swap per i miei tipi , o sarà std::swap essere efficiente come si ottiene, a condizione che la mia classe definisce dove costruttore e un operatore di assegnazione del movimento, ovviamente?

+0

Voglio dire che questo è stato menzionato da Scott o Herb a _C++ e Beyond_, ma non riesco a trovare nulla a riguardo. –

+2

Correlati: [Sposta semantica == funzione di scambio personalizzata obsoleta?] (Http://stackoverflow.com/questions/6416385/move-semantics-custom-swap-function-obsolete) – Xeo

+0

@Xeo: Grazie, mi ero completamente dimenticato di quella domanda/risposta. Ricevo punti per essere coerente? :-) Sono sicuro che sarebbe stato rosso in faccia altrimenti! –

risposta

30

La specializzazione di std::swap è ora facoltativa, ma non deprecato. La logica è la prestazione.

Per il codice di prototipazione, e forse anche per molto codice di spedizione, std::swap sarà molto veloce. Tuttavia, se ti trovi in ​​una situazione in cui hai bisogno di trarre il massimo dal tuo codice, scrivere uno scambio personalizzato può comunque rappresentare un notevole vantaggio in termini di prestazioni.

Considerare il caso in cui la classe ha essenzialmente un puntatore proprietario e il costruttore di movimento e l'assegnazione di spostamento devono semplicemente occuparsi di quel puntatore.Conta carichi e depositi per ogni membro:

Costruttore di spostamento: 1 carico e 2 negozi.

Spostamento assegnazione: 2 carichi e 2 negozi.

Swap personalizzato: 2 carichi e 2 negozi.

std::swap è una costruzione di spostamento e due spostamenti, oppure: 5 carichi e 6 negozi.

Uno scambio personalizzato è potenzialmente ancora due o tre volte più veloce di std::swap. Anche se in qualsiasi momento stai cercando di capire la velocità di qualcosa contando carichi e negozi, entrambi saranno presto malvagi.

Nota: nel calcolo del costo dell'assegnazione del movimento, accertarsi che si passi al valore spostato da (nell'algoritmo std::swap). Questo spesso nega il costo di una deallocazione, anche se a costo di una filiale.

+7

Non dovrebbe essere possibile per il compilatore incorporare le assegnazioni di spostamento e rimuovere gli archivi morti? – u0b34a0f6ae

+7

Non sono un ingegnere del compilatore. Direi che è possibile, ma non lo so per certo. Sarei esitante a dipendere da questo. Alla fine della giornata dovrai codificarlo in entrambi i modi e testare. E se il tuo test indica che 'std :: swap' è altrettanto veloce, quel risultato potrebbe essere dipendente dal compilatore/dalla piattaforma. E se la tua applicazione non esegue il "swap" in una regione con prestazioni critiche, probabilmente non ti interesserà se 'std :: swap' è due volte più lento di quanto non sia veloce. –

+1

Arriva un po 'in ritardo alla festa ... Ma ho un semplice dubbio: perché Move Assignment ha contabilizzato per 2Loads + 2Stores (1 carico in più rispetto a Move Constructor)? È perché deve fare "return * this"? O a causa della necessità implicita di "eliminare" il puntatore proprietario prima di riassegnarlo, e quindi deve essere letto nell'espressione di cancellazione? – abigagli

0

Dipende dai tipi.

Lo si sposta da x a z, da y a x, da z a y. Ecco tre operazioni di copia della rappresentazione sottostante (forse solo un puntatore, forse qualcosa di più, chissà)

Ora forse puoi creare uno scambio più veloce per il tuo tipo (xor swap trick, inline assembler o forse std :: scambiare per i tuoi tipi sottostanti è solo più veloce).

O forse anche il compilatore è in grado di ottimizzare e in sostanza ottimizza entrambi i casi nelle stesse istruzioni (come il temporaneo in un registro).

Personalmente tendo ad implementare sempre una funzione membro di scambio che verrà chiamata da più punti, comprese le cose come l'assegnazione del movimento, ma YMMV.

0

Questo swap() chiama un costruttore di trasloco e 2 assegnazioni di spostamento. Penso che si può scrivere più efficiente swap() per il suo particolare tipo di classe come,

class X 
{ 
    int * ptr_to_huge_array; 
public: 
// ctors, assgn ops. etc. etc. 

    friend void swap(X& a, X& b) 
    { 
     using std::swap; 
     swap(a.ptr_to_huge_array, b.ptr_to_huge_array); 
    } 
}; 

indipendentemente l'attuazione di movimento costruttore e operatore di assegnazione.

+0

Dov'è il costruttore di copie? Potresti dare un esempio di uno scambio più efficiente? – ronag

+0

@ronag: Mi dispiace, mio ​​errore. –

2

è specializzata std :: scambio deprecato ora che abbiamo la semantica mossa?

No. Questa è la versione generica, ma è possibile ottimizzarla per saltare una terza operazione di spostamento. La mia preferenza è quella di combinare copia dello swap idioma & con la personalizzazione di std :: swap per le mie classi.

Ciò significa che avrò:

class Aaaa 
{ 
public: 
    Aaaa(); // not interesting; defined elsewhere 
    Aaaa(Aaaa&& rvalueRef); // same 
    Aaaa(const Aaaa& ref); // same 
    ~Aaaa(); // same 
    Aaaa& operator=(Aaaa object) // copy&swap 
    { 
     swap(object); 
     return *this; 
    } 
    void swap(Aaaa& other) 
    { 
     std::swap(dataMember1, other.dataMember1); 
     std::swap(dataMember2, other.dataMember2); 
     // ... 
    } 

    // ... 
}; 

namespace std 
{ 
    template<> inline void std::swap(Aaaa& left, Aaaa& right) 
    { left.swap(right); } 
} 
+3

Il modo corretto di usare 'swap' è di' usare std :: swap' seguito da una chiamata non qualificata a 'swap'. Inoltre, la specializzazione di 'std :: swap' è vista come Not So Good ™, perché non è possibile specializzare parzialmente le funzioni. Vedi [questa mia risposta] (http://stackoverflow.com/questions/6380862/how-to-provide-a-swap-function-for-my-class/6380882#6380882) per ulteriori dettagli. – Xeo

+0

È consentito aggiungere qualcosa allo spazio dei nomi std? –

+2

@MichaWiedenmann, è possibile aggiungere specializzazioni di modelli di funzioni std (come nell'esempio precedente) – utnapistim