c++ smart-pointers stdvector move-semantics

c++ - El compilador no falla al empujar un std:: unique_ptr en un std:: vector



smart-pointers stdvector (4)

Con C ++ 11 conseguimos mover constructores y valores semánticos.

std :: move (X) es solo un lanzamiento a un valor que convierte X en X &&, eso es todo. Luego de mover, ctor retoma el trabajo y mueve a los constructores, por lo general, "roban" los recursos en poder del argumento. unique_ptr tiene un ctor de movimiento.

Los valores de retorno de la función ya son un valor r (a menos que la función devuelva una referencia de valor l como se indica en @HolyBlackCat en los comentarios) que activará el ctor de movimiento sin necesidad de una conversión adicional. Y dado que move ctor está definido para unique_ptr, se compilará.

También la razón por la que v.push_back (p1); falla es: intenta llamar al constructor de copia con un lvalue y falla porque unique_ptr no tiene un ctor de copia.

Un unique_ptr no se puede reenviar en un std::vector ya que no se puede copiar, a menos que se use std::move . Sin embargo, seamos una función que devuelva un unique_ptr , entonces, la operación std::vector::push_back(F()) está permitida. Hay un ejemplo a continuación:

#include <iostream> #include <vector> #include <memory> class A { public: int f() { return _f + 10; } private: int _f = 20; }; std::unique_ptr<A> create() { return std::unique_ptr<A>(new A); } int main() { std::unique_ptr<A> p1(new A()); std::vector< std::unique_ptr<A> > v; v.push_back(p1); // (1) This fails, should use std::move v.push_back(create()); // (2) This doesn''t fail, should use std::move? return 0; }

(2) está permitido, pero (1) no está permitido. ¿Es esto porque el valor devuelto se mueve de alguna manera implícita?

En (2) , ¿es realmente necesario usar std::move ?


También vale la pena saber que también funcionaría debido a la capacidad del compilador para mover objetos que no se mueven explícitamente (NRVO)

#include <iostream> #include <vector> #include <memory> class A { public: int f() { return _f + 10; } private: int _f = 20; }; std::unique_ptr<A> create() { std::unique_ptr<A> x (new A); return x; } int main() { std::unique_ptr<A> p1(new A()); std::vector< std::unique_ptr<A> > v; //v.push_back(p1); // (1) This fails, should use std::move v.push_back(create()); // (2) This doesn''t fail, should use std::move? return 0; }


std::vector::push_back() tiene una sobrecarga que toma una referencia rvalue como entrada:

void push_back( T&& value );

El valor de retorno de create() es un temporal sin nombre, es decir, un rvalue, por lo que se puede pasar como está a push_back() sin necesidad de usar std::move() en él.

std::move() es necesario solo cuando se pasa una variable nombrada, es decir, un valor l, donde se espera un valor r.


std::move(X) significa esencialmente "aquí, trata a X como si fuera un objeto temporal".

create() devuelve un std::unique_ptr<A> temporal std::unique_ptr<A> para comenzar, por lo que move es necesario.

Si quieres saber más, mira en las categorías de valor . Su compilador usa categorías de valor para determinar si una expresión se refiere a un objeto temporal ("rvalue") o no ("lvalue").

p1 es un lvalue, y create() es un rvalue.