Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::move into static_pointer_cast: Why doesn't static_pointer_cast have an rvalue reference overload?

Tags:

c++

c++11

Suppose we have a fuction expecting a shared pointer by value. (In a real life example I take it by rvalue reference and forward it to a member.)

void f(std::shared_ptr<Derived> ptr) { ... }

But we only have a shared pointer to the base class, so we use static_pointer_cast:

std::shared_ptr<Base> ptr = std::make_shared<Derived>();
f(std::static_pointer_cast<Derived>(ptr));

Does the first assignment (construction of ptr from the temporary) trigger an atomic increment and decrement of the reference count or is the shared pointer moved? (Note that it's being up-casted.)

Within the static_pointer_cast there is an atomic increment of the reference count. In case we don't need ptr anymore, we'd want to move it into f. But as there is no overload of static_pointer_cast taking an rvalue reference, the move won't have any effect:

f(std::static_pointer_cast<Derived>(std::move(ptr)));

We still have the atomic increment and the corresponding atomic decrement as soon as ptr is destructed. Why is there no such overload?

like image 649
haslersn Avatar asked May 03 '17 22:05

haslersn


1 Answers

I can answer the first part of your question, but not the second. While I'm not sure whether it's mandated by the standard, I'm pretty sure that:

std::shared_ptr<Base> ptr = std::make_shared<Derived>();

Will not do any extraneous refcounter increments/decrements. First, let me observe that this is actually not an assignment at all, but construction of ptr. Clearly it's been constructed by a temporary, and clearly the temporary is of a different type. The signature of the constructor that will be matched is (http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr):

template< class Y > 
shared_ptr( shared_ptr<Y>&& r );

In the notes it says:

Move-constructs a shared_ptr from r. After the construction, *this contains a copy of the previous state of r, r is empty and its stored pointer is null. The template overload doesn't participate in overload resolution if Y* is not implicitly convertible to T*

In this case, Y is Derived and T is Base, so clearly we get implicit conversion from Y* to T*, so the constructor is legal. Strictly speaking it might be conforming to allow the reference count to first rise to 2, and then drop back down to 1. But obviously this defeats the whole purpose of a move constructor so I rather strongly doubt this is how it is implemented.

like image 148
Nir Friedman Avatar answered Oct 31 '22 23:10

Nir Friedman