2012-03-24 9 views
6

Me preguntaba en qué tipo de casos tiene sentido usar la semántica de movimiento al sobrecargar operador + y/u operador + =. A pesar de que se explica en this question cómo se puede hacer esto, no puedo entender por qué lo haría. Consideremos el operador + =. Si solo paso el lado derecho por referencia y hago los cambios apropiados en el objeto del lado izquierdo, de todos modos no hay copias innecesarias. Entonces, volvemos al mismo punto: ¿la semántica del movimiento sería beneficiosa en tal caso?¿Tiene sentido usar la semántica de movimiento para el operador + y/o el operador + =?

+0

En general, es una mala idea sobrecargar a los operadores. Porque: ** 1) ** si es obviamente un buen caso para hacerlo, probablemente haya una biblioteca que ya lo esté implementando. ** 2) ** si no es obviamente un buen caso para hacerlo, probablemente sea un muy mal caso para hacerlo. – enobayram

+3

@enobayram lo convierten en una función de miembro 'append' si se trata de unos operadores escandalosos sobrecargados. Ahora todavía puedes responder la pregunta. –

+7

@enobayram, eso no tiene ningún sentido en absoluto. En esa lógica, generalmente es una mala idea escribir código, de cualquier tipo, alguna vez. – TonyK

Respuesta

12

Sí y no.

operador semántica + =

movimiento no son necesariamente útiles para operator+= en general, debido a que ya está modificando el argumento lado izquierdo (this), por lo que ya tiene recursos para trabajar con la mayoría de las veces.

Aún así, como una optimización, podría valer la pena. Imagine una implementación std::string cuyo constructor predeterminado no asigna ninguna memoria. Entonces std::string::operator+=(std::string&&) podría simplemente robar los recursos de RHS. O imagina que el buffer RHS es lo suficientemente grande como para contener todo pero el LHS no lo es, entonces si puedes usar el buffer RHS estás dorado: simplemente intercambia y precede.

Por lo tanto, puede valer la pena, pero tiene que estudiarlo. Por lo tanto:

  • T& T::operator+=(T const&): siempre está presente
  • T& T::operator+=(T&&): para permitir la semántica movimiento cuando tiene sentido

operador +

Aquí siempre es útil (siempre estamos hablando de clases para lo cual la semántica del movimiento es útil).

La cosa es, operator+ produce un temporal (de la nada) por lo que generalmente tiene que crear recursos para este temporal. Sin embargo, si puede robarlos en lugar de crearlos, sin duda es más barato.

Sin embargo, no es necesario proporcionar todas las sobrecargas:

  • T operator+(T const&, T const&)
  • T operator+(T&&, T const&)
  • T operator+(T const&, T&&)
  • T operator+(T&&, T&&) (requerido para la desambiguación)

No, puede volver a utilizar la misma truco que operator= use y cree el derecho temporal en la firma de la función (tomando un argumento por copia). Si el tipo es movible, se llamará al constructor de movimientos, de lo contrario será el constructor de copias, pero como necesita el temporal de todos modos, no habrá pérdida de rendimiento.

inline T operator+(T left, T const& right) { left += right; return left; } 
inline T operator+(T const& left, T right) { right += left; return right; } // commutative 
inline T operator+(T left, T&& right) { left += right; return left; } // disambiguation 

No hay mucho que ganar (3 en lugar de 4) pero bueno, ¡tomaré lo que pueda!

Por supuesto, para cadena, operator+ no es conmutativo (por lo que es una mala sobrecarga), por lo que la implementación real de la segunda sobrecarga requeriría un método prepend.

EDIT: siguiendo Move semantics and operator overloading parece que estaba un poco demasiado entusiasta. El robo de la respuesta de Ben Voigt, obtenemos:

inline T operator+(T left, T const& right) { left += right; return left; } 
inline T operator+(const T& left, T&& right) { right += left; return right; } 

Por otro lado, esto sólo parece funcionar para las operaciones conmutativas; - no funciona de esa manera, pero probablemente se puede adaptar, / y % por otro lado ...

+0

Mejor haz 'derecha + = izquierda; return right; 'para que considere' right' como un valor de referencia al regresar. –

+0

@ JohannesSchaub-litb: ¿Puedo suponer que estás hablando del último caso? También me pregunté sobre esto y si valía la pena sobrecargar 'T && T :: operator +() &&'. –

+0

Estoy hablando de los tres casos. Si 'T' tiene un constructor de movimiento, puede hacer uso de él retornando' left' o 'right' respectivamente. Como está ahora con su código, si 'operator + =' devuelve 'T &', siempre copiará el parámetro en el valor de retorno en lugar de moverlo. –

1

Si agrega dos cadenas, vectores, etc. que no puede "mover", no tiene sentido. Pero si está agregando, digamos listas enlazadas, donde agregar la lista es posiblemente un operador O (1), si está dispuesto a sacrificar el lado derecho, entonces tiene sentido.

Cuestiones relacionadas