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 + =?
Respuesta
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á presenteT& 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 ...
Mejor haz 'derecha + = izquierda; return right; 'para que considere' right' como un valor de referencia al regresar. –
@ 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 +() &&'. –
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. –
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.
- 1. Semántica de movimiento para un operador de conversión
- 2. Usar el operador ternario para operaciones múltiples
- 3. C++ múltiples sobrecargas de operador para el mismo operador
- 4. ¿Por qué el operador postfix ++ tiene mayor prioridad que el operador prefijo ++?
- 5. Reconociendo cuándo usar el operador de módulo
- 6. operador?: (El "operador de Elvis) en PHP
- 7. ¿Por qué el operador% se conoce como el operador "módulo" en lugar del operador "restante"?
- 8. reemplazo Lua para el operador%
- 9. ¿Para qué sirve el operador "==="?
- 10. movimiento semántica std :: mover
- 11. Restando dos números sin usar el operador '-'
- 12. ¿Tiene sentido usar Web Workers para un juego?
- 13. define el operador void * y el operador bool
- 14. ¿Puedo usar el operador [] en C++ para crear matrices virtuales
- 15. ¿Usar el operador ternario para inicializar una variable de referencia?
- 16. Ternario? operador contra el operador convencional Si-else en C#
- 17. ¿Cuándo sobrecarga el operador nuevo?
- 18. Semántica de -> operador en listas (y en general C++)
- 19. "es" - el operador para el tipo
- 20. Definir variable para usar con el operador IN (T-SQL)
- 21. ¿Usar listas de estructuras tiene sentido en el cacao?
- 22. ¿Puedo usar el operador 'y' en xsl para cada uno?
- 23. Cómo usar el operador% para valores float en c
- 24. ¿Cómo anular el operador []?
- 25. El operador de Mod en ios
- 26. Cuándo usar Class.isInstance() y cuándo usar el operador instanceof?
- 27. ¿El iterador admite + operador?
- 28. Imitando el operador "IN"
- 29. Implementar operador de conversión para el puntero
- 30. Semántica de movimiento == función de intercambio personalizada obsoleta?
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
@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. –
@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