Reputation: 3567
I often see the following idiom in production code: A value argument (like a shared pointer) is handed into a constructor and shall be copied once. To ensure this, the argument is wrapped into a std::move
application. Hating boilerplate code and formal noise I wondered if this is actually necessary. If I remove the application, at least gcc 7 outputs some different assembly.
#include <memory>
class A
{
std::shared_ptr<int> p;
public:
A(std::shared_ptr<int> p) : p(std::move(p)) {} //here replace with p(p)
int get() { return *p; }
};
int f()
{
auto p = std::make_shared<int>(42);
A a(p);
return a.get();
}
Compiler Explorer shows you the difference. While I am not certain what is the most efficient approach here, I wonder if there is an optimization that allows to treat p
as a rvalue reference in that particular location? It certainly is a named entity, but that entity is "dead" after that location anyway.
Is it valid to treat a "dead" variable as a rvalue reference? If not, why?
Upvotes: 2
Views: 460
Reputation: 180295
In the body of the constructor, there are two p
objects, the ctor argument and this->p
. Without the std::move
, they're identical. That of course means the ownership is shared between the two pointers. This must be achieved in a thread-safe way, and is expensive.
But optimizing this out is quite hard. A compiler can't generally deduce that ownership is redundant. By writing std::move
yourself, you make it unambiguously clear that the ctor argument p
does not need to retain ownership.
Upvotes: 2