Reputation: 5073
Lets consider the following piece of code:
template<typename T>
void f(std::unique_ptr<T>&& uptr) { /*...*/ }
In another function:
void g()
{
std::unique_ptr<ANY_TYPE> u_ptr = std::make_unique<ANY_TYPE>();
f(std::move(u_ptr));
X: u_ptr->do_sth(); // it works, I don't understand why. Details below.
}
I don't understand why u_ptr
in line X
is still alive.
After all I forced him to be moved (std::move).
---EDIT---
Ok, so now:
The code is still working:
class T{
public:
T(){}
void show(){
std::cout << "HEJ!\n";
}
};
void f(std::unique_ptr<T> ref){
ref->show();
}
int main()
{
std::unique_ptr<T> my;
my->show();
f(std::move(my));
my->show(); // How is it possible. Now, f takes unique_ptr by value
return 0;
}
Upvotes: 0
Views: 426
Reputation: 3527
The reason why your call to show
doesn't crash is because it doesn't use the this
pointer (it doesn't try to modify or access a data member).
Try this:
class T{
public:
int v;
T(){}
void show(){
v = 0;
std::cout << "HEJ!\n";
}
};
void f(std::unique_ptr&& ref)
This is the answer when you initially had your f
function taking a rvalue reference &&.
Your function takes a rvalue reference. Therefore, no new unique_ptr
object is created yet, you are simply passing a reference
.
Inside your f
function, if you create a a local unique_ptr
, with the parameter uptr
, then finally uptr
will be moved to create that new object.
template<typename T>
void f(std::unique_ptr<T>&& uptr)
{
//move uptr into local_unique_ptr
//note that we have to use move again
//because uptr has a name, therefore its a lvalue.
auto local_unique_ptr = std::unique_ptr<T>(std::move(uptr));
}
The important thing to always know is that std::move
is simply a static_cast
.
If you pass a lvalue
to std::move
, it returns a rvalue
. If you pass a rvalue
, it returns a rvalue
. That's it.
Upvotes: 1
Reputation: 182875
You didn't show us that code to function f
, but presumably it didn't move the pointer, even though it had permission to.
You passed the unique_ptr
by reference. If function invocation actually moved it, then the function couldn't use it because it would be gone before the function had a chance to.
If you want function invocation to actually move the pointer, you need to pass the pointer by value, not be reference. That value would be a unique_ptr
for it to be moved into. In that case, you should declare the function as taking a std::unique_ptr<T>
instead of a std::unique_ptr<T>&&
. Then you can actually invoke the move constructor when you call the function.
Update: With your latest change, the unique_ptr
would no longer reference any valid object due to the move construction. You just never check that it does. Invoking a non-virtual method that doesn't access any member variables can work just the same whether the object is valid or destroyed because it doesn't need anything from the object. You also never made the unique_ptr
actually point to anything.
Instead, make the unique_ptr
point to something. After it's moved, try calling a virtual function or accessing a member whose value is changed by the destructor. Like this:
#include <iostream>
#include <memory>
class T{
public:
T() : valid (true) {}
~T() { valid = false; }
bool valid;
void show(){
std::cout << "HEJ! " << valid << std::endl;
}
};
void f(std::unique_ptr<T> ref){
ref->show();
}
int main()
{
std::unique_ptr<T> my (new T); // Make it point to a new object
my->show();
f(std::move(my));
my->show(); // Try to access
return 0;
}
Upvotes: 1
Reputation: 26536
in the line f(std::unique_ptr<T>&& uptr)
uptr
is not an object - it's a reference. a reference which capable to catch temporeries and mutate them.
it's like asking why doesn't the object get cloned in the next example
void func(std::string& str);
std::string str_ = "yyy";
func(str_);
str_
is passed by "regular" reference and won't get copied - this is what pass by reference means.
std::move
only cast l-value to r-value-reference, which uptr
in f(std::unique_ptr<T>&& uptr)
can reference, it's a reference referencing an object. opposed to the common conception, std::move
won't do any moving by itself, only casts the object to r-value-reference for the move constructor/assg. operator to kick in.
here, the pointer still holds valid data since it was not moved, only casted to r-value-reference.
if you want the object to move you have to declare the parameter as object, not reference : f(std::unique_ptr<T> uptr)
In your edit, you have undefiend behaviour, so everything may occure.
Upvotes: 1
Reputation: 136515
Your function f
may not in fact move the pointer. Merely taking an object by &&
does not modify the object.
u_ptr->do_sth()
may invoke a static member function or a member function that does not access the object (this
) and this is why it does not crash.
Upvotes: 0