Movimiento
Desde su primer post, he seguido el blog de C++Next de Dave Abrahams (y otros). Empezaron con la conmoción de los conceptos, pero ahora se están dedicando a la optimización mediante movimiento en vez de copia.
La idea es simple: copiar es caro, movamos. La diferencia entre mover A a B y copiar A en B es que, al mover, podemos tocar A y robarle los recursos. Supongamos que el objeto A internamente apunta a otro objeto X. Si copio A en B, debo hacer una copia del ojeto interno X. Si muevo A a B, le quito el objeto X a A y se lo paso a B.
Originalmente: A--->X, B
Tras copia: A--->X, B---->Y (Y es copia de X)
Tras movimiento: A, B---->X
El problema es que, tras mover A, queda en un estado que podíamos llamar de "desguazado" lo que lo hace, en principio, inusable. ¿Cómo saber si puedo mover desde A de forma que, aunque quede desguazado, no haya problemas?
Temporalidad
La idea vuelve a ser sencilla: los temporales pueden desguazarse porque no se van a usar de nuevo. Los temporales aparecen en expresiones como la siguiente.
c=a+b
El resultado de a+b se tiene que guardar en algún sitio para luego copiarlo a c. Es decir, se tiene que usar un temporal.
t:=a+b //Este := no copia, es más bien "pon el resultado en t"
c=t //Aquí sí que hay copia
El gran problema es que el operador ":=" que me he inventado es interno al compilador. De hecho, incluso el compilador no lo usará en muchos casos. Por ejemplo, cuando "a" y "b" sean valores que quepan en los registros de la máquina no le importará copiar porque sabe que es una operación barata. Si "a" y "b" son matrices, entonces sí que tendrá que reservar el espacio correspondiente a "t" para "poner el resultado en t".
Lo bueno que tienen los temporales es que son de usar y tirar. Así que lo de arriba podría hacerlo así.
t:=a+b
c=t //Mueve, desguazando "t" porque se va a destruir a continuación.
Esta es la aproximación al problema que usa C++. Introduce unas referencias a valores temporales (r-values) de manera que, si el programador usa estas referencias, sabe que se puede mover.
Unicidad referencial
Otro aspecto interesante de los valores temporales es que son únicos en el sentido de que no hay otra referencia a ellos. Sin embargo, ¿podremos generalizar esto? Estoy pensando en el sistema de propiedad de Bartosz Milewski y si hay alguna relación con el movimiento.
El sistema de BM es muy simple. Cada objeto tiene un dueño. Este dueño es responsable de alguna tarea o un valor que indique que no es necesaria la tarea. Ejemplos de tarea son la tarea de sincronizar los accesos entre distintas hebras y la tarea de borrar el objeto una vez se haya acabado de trabajar con él.
En el caso de la sincronización (que es de lo que se encarga BM) un dueño podrá ser: un objeto que sincronize (monitor), un valor que indique que ese objeto no necesita sincronización. De hecho, hay varios valores: porque el objeto es inmutable, porque el objeto es local a la hebra o porque el objeto está referenciado en un único sitio.
¿He dicho en un único sitio? Si el objeto está referenciado en un único sitio significa que
- No es necesaria la sincronización porque sólo una única hebra podrá modificarlo. Esto no es muy relevante para nuestra discusión aquí.
- Es fácil de controlar el desguace ya que sabemos que a partir del punto de movimiento el objeto no debe ser usado. Es decir, deberíamos requerir unicidad referencial para tomar el objeto como temporal y poder moverlo sin peligro.
- Tenemos que mover la referencia para que ésta siga siendo única (copiar la referencia haría que no fuera única) y por tanto hemos de tomar la referencia única como un temporal para esto.
Es muy curiosa la relación que hemos encontrado entre estas tres ideas. Todavía no tengo muy claras las cosas, tengo que seguir analizando qué pasa y por qué pasa, pero creo que todo esto es la punta de un gran iceberg.