¿Por qué a = (a + b) – (b = a) es una mala elección para intercambiar dos enteros?

Los resultados no están definidos porque los efectos secundarios de b = a pueden ocurrir en cualquier punto antes del siguiente punto de secuencia que es el punto y coma.

Dima Korolev es a^=b, b^=a, a^=b; funciona porque las comas son puntos de secuencia.

En general, desea darle al compilador la oportunidad de hacer lo correcto y solo micro-optimizar cuando mide un problema de rendimiento que vale la pena solucionar porque de lo contrario perderá mucho tiempo tanto el suyo como el de otras personas.

En este caso, std :: swap debería expandirse en línea a lo que funcione mejor para el procesador en cuestión y no lo va a superar.

Incluso si ese no fuera el caso, no sería mejor en la mayoría de los procesadores que la asignación con uno o más temporarios en los registros porque los conjuntos de instrucciones generalmente solo admiten un operando de memoria y hay paradas de carga.

Para fines ilustrativos, he citado la salida (sintaxis de AT&T con el operando de origen y luego de destino, que es lo opuesto a MASM) de gcc 4.7 amd64 con -O en varias implementaciones de void swap(int &lhs, int &rhs) de void swap(int &lhs, int &rhs) .

Nota:

  1. La versión con asignación simple y una variable temporal es idéntica a std :: swap.
  2. El compilador utiliza un par de registros en lugar de una sola variable temporal para limitar el impacto de pérdida de carga en ambos casos con dos instrucciones entre la carga de lhs y el almacén de rhs.
  3. La versión que usa xor tiene un almacenamiento y carga adicionales y funcionará peor de lo que sugiere el recuento de instrucciones porque los valores leídos de la memoria se usan inmediatamente después de la recuperación, por lo que nada se superpone con el retraso de carga (4 ciclos desde la caché L1 en los procesadores i7) y la pérdida de carga Los impactos se maximizan. El código permanece igual con -O9.
  std :: swap (lhs, rhs);

 movl (% rdi),% eax
 movl (% rsi),% edx
 movl% edx, (% rdi)
 movl% eax, (% rsi)
  int tmp;
 tmp = lhs;
 lhs = rhs;
 rhs = tmp;  

 movl (% rdi),% eax
 movl (% rsi),% edx
 movl% edx, (% rdi)
 movl% eax, (% rsi)

  lhs ^ = rhs, rhs ^ = lhs, lhs ^ = rhs;

 movl (% rsi),% eax
 xorl (% rdi),% eax
 movl% eax, (% rdi)
 xorl (% rsi),% eax
 movl% eax, (% rsi)
 xorl% eax, (% rdi)

Porque requiere más energías mentales para analizar y deja más espacio para el error en comparación con std::swap(a, b) .

Editar : a^=b, b^=a, a^=b; Es malo por las mismas razones.

El desbordamiento es uno. Las suposiciones sobre el orden de evaluación es otra.

¡Este método no le dará respuesta si los valores de a y b son enormes!
Como MAX_INT es el valor máximo de entero que se puede almacenar en a y b. En el momento en que agreguemos a & b, o los multipliquemos, se desbordarán. Obtendremos WA (respuesta incorrecta).

Así que es mejor atenerse a lo básico … es decir, usar variables temporales.
O un regalo para los programadores de Python a, b = b, a”

Agrega cálculos adicionales que pueden desbordarse y depender en gran medida de una determinada interpretación del estándar C / C ++. Además, +, – y = no se aplican a parámetros no numéricos, por lo que es posible que tenga más de lo que puede masticar.

Es un comportamiento indefinido. Lo cual es una mala elección para cualquier cosa . Aparte de demostrar un comportamiento indefinido, tal vez.