Comprendiendo el movimiento semántico para un shared_ptr con una llamada de función rvalue.
En el siguiente programa pequeño tengo dos ejemplos de uso de move
con shared_ptr
.
El primer ejemplo se comporta como esperaba y la propiedad del shared_ptr
p
se le asigna al nuevo puntero p2
. Después de la asignación, p
es un puntero inválido.
Esperaría que lo mismo suceda también en el segundo ejemplo, pero no lo hace. Las preguntas precisas están incrustadas como comentarios en el código fuente. ¿Qué está mal en mi razonamiento?
“`cpp
#include <memory>
#include <iostream>
using namespace std;
void foo(shared_ptr<int>&& p)
{
std::cout << "zoo: " << p.use_count() << "\n";
// cuando esta función termina, ¿el destructor de p debería
// decrementar el contador de referencias y destruir el objeto apuntado?
}
void example1()
{
auto p = make_shared<int>(0);
std::cout << "count before move: " << p.use_count() << "\n";
shared_ptr<int> p2(move(p));
std::cout << "count after move: " << p.use_count() << "\n"; // output 0: propiedad transferida a p2
}
void example2()
{
auto p = make_shared<int>(0);
std::cout << "count before move: " << p.use_count() << "\n";
foo(move(p));
std::cout << "count after move: " << p.use_count() << "\n";
// output 1: ¿Por qué esto no es cero?
// ¿Por qué la propiedad no se ha transferido al argumento de la función foo?
}
int main()
{
example1();
example2();
return 0;
}
“`
davy.ai
La razón por la cual la propiedad no ha sido transferida al argumento de la función
foo
en el segundo ejemplo es porquep
dentro defoo
no es de tiposhared_ptr<int>
, sinoshared_ptr<int>&&
, que es una referencia de valor temporal (rvalue reference). Por lo tanto, el argumento es tratado como un rvalue ymake_shared<int>(0)
crea un nuevo objeto temporal, que luego se mueve para inicializar un nuevoshared_ptr<int>
dentro defoo
. Este nuevo puntero compartido es distinto dep
enmain
, por lo que después de quefoo
sale, el recuento de referencias dep
enmain
sigue siendo 1 y la memoria apuntada porp
no se libera.Para transferir la propiedad a
foo
, necesitamos modificar su firma para que tome el argumento por valor en lugar de una referencia de valor temporal (rvalue reference):Con este cambio,
p
dentro defoo
es una copia del puntero compartido demain
, por lo que cuandofoo
sale,p
se destruye y transfiere la propiedad de vuelta alp
original enmain
, lo que hace que su recuento de referencias llegue a cero y la memoria apuntada porp
se libere.