2011-12-23 10 views
32

Duplicar posible:
Move semantics == custom swap function obsolete?¿Se está desaprobando el uso de std :: swap ahora que tenemos semántica de movimiento?

Así es como std::swap se ve como en C++ 11:

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

¿Todavía tengo especializarse std::swap para mis propios tipos , o será std::swap ser tan eficiente como sea posible, siempre que mi clase defina am ove constructor y un operador de asignación de movimiento, por supuesto?

+0

Quiero decir que esto fue mencionado por Scott o Herb en _C++ y Beyond_, pero parece que no puedo encontrar nada al respecto. –

+2

Relacionado: [Mover semántica == función de intercambio personalizada obsoleta?] (Http://stackoverflow.com/questions/6416385/move-semantics-custom-swap-function-obsolete) – Xeo

+0

@Xeo: Gracias, me había olvidado por completo de esa pregunta/respuesta. ¿Obtengo puntos por ser consistente? :-) ¡De lo contrario hubiera sido pelirrojo! –

Respuesta

30

La especialización de std::swap ahora es opcional, pero no obsoleta. La razón es el rendimiento.

Para el código de creación de prototipos, y quizás incluso para muchos códigos de envío, std::swap será mucho más rápido. Sin embargo, si se encuentra en una situación en la que necesita buscar cada bit de su código, escribir un intercambio personalizado puede ser una gran ventaja de rendimiento.

Considere el caso en el que su clase tiene esencialmente un puntero propietario y el constructor de movimiento y la asignación de movimiento solo tienen que ocuparse de ese puntero.Contar las cargas y las tiendas de la máquina para cada miembro:

Mover constructor: 1 carga y 2 tiendas.

Asignación de movimiento: 2 cargas y 2 tiendas.

Intercambio personalizado: 2 cargas y 2 tiendas.

std::swap es 1 movimiento de construcción y 2 asignaciones de movimiento, o: 5 cargas y 6 tiendas.

Un intercambio personalizado es potencialmente aún dos o tres veces más rápido que std::swap. Aunque cada vez que intentas averiguar la velocidad de algo contando cargas y tiendas, ambos van a ser perversos rápidamente.

Nota: Al calcular el costo de su asignación de movimiento, asegúrese y tenga en cuenta que se moverá a un valor movido (en el algoritmo std::swap). Esto a menudo niega el costo de una desasignación, aunque a costa de una sucursal.

+7

¿No debería ser posible que el compilador alinee las asignaciones de movimiento y elimine las tiendas muertas? – u0b34a0f6ae

+7

No soy un ingeniero compilador. Supongo que eso es posible, pero no estoy seguro. Yo dudaría en depender de eso. Al final del día, tendrá que codificarlo en ambos sentidos y probarlo. Y si su prueba indica que 'std :: swap' es igual de rápido, ese resultado puede ser dependiente del compilador/plataforma. Y si su aplicación no "cambia" en una región de desempeño crítico, entonces probablemente no le importe si 'std :: swap' es dos veces más lento que el rápido. –

+1

Llegando un poco tarde a la fiesta ... Pero tengo una duda simple: ¿por qué Move Assignment representó 2Loads + 2Stores (1 Load más que Move Constructor)? ¿Es porque tiene que hacer "devolver * esto"? ¿O debido a la necesidad implícita de "eliminar" el puntero propietario antes de reasignarlo, por lo que debe leerse en la expresión de eliminación? – abigagli

0

Eso dependerá de sus Tipos.

Lo moverá de x a z, de y a x, de z a y. Es decir, tres operaciones de copia de la representación subyacente (tal vez solo un puntero, tal vez algo más, quién sabe)

Ahora quizás pueda crear un intercambio más rápido para su tipo (xor swap truick, inline assembler, o tal vez std :: el intercambio para los tipos subyacentes es más rápido).

O tal vez también su compilador es bueno en la optimización, y esencialmente optimiza ambos casos en las mismas instrucciones (como tener el temporal en un registro).

Yo personalmente tiendo a implementar siempre una función de miembro de intercambio que se llamará desde varios lugares, incluyendo cosas como asignación de movimiento, pero YMMV.

0

Este swap() llama a un constructor de movimiento y 2 tareas de movimiento. Creo que se puede escribir más eficiente swap() por su particular tipo de clase como,

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); 
    } 
}; 

independientemente de la aplicación de movimiento constructor y operador de asignación.

+0

¿Dónde está el constructor de copia? ¿Podría dar un ejemplo de un intercambio más eficiente? – ronag

+0

@ronag: Lo siento, mi error. –

2

¿Se está desaprobando el uso de std :: swap ahora que tenemos semántica de movimiento?

No. Esta es la versión genérica, pero puede optimizarla para omitir una operación de tercer movimiento. Mi preferencia es combinar la copia & idioma de intercambio con la personalización de std :: swap para mis clases.

Eso significa que tendré:

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

La forma correcta de usar 'swap' es' using std :: swap' seguido de una llamada no calificada a 'swap'. Además, la especialización de 'std :: swap' se ve como Not So Good ™, porque no puedes especializar parcialmente las funciones. Ver [esta respuesta mía] (http://stackoverflow.com/questions/6380862/how-to-provide-a-swap-function-for- my-class/6380882#6380882) para más detalles. – Xeo

+0

¿Puede agregar algo al espacio de nombres std? –

+2

@MichaWiedenmann, puede agregar especializaciones de plantilla de funciones estándar (como en el ejemplo anterior) – utnapistim

Cuestiones relacionadas