Home > other >  moving the content of a unique_ptr
moving the content of a unique_ptr

Time:04-14

So if I have a moveable (rval) std::unique_ptr, can I use simple assign to move the contents rather than copy? (The contents of the pointed to object, rather than the pointer to it)

It doesn't look like I can, but "maybe I'm doing it wrong"?

If not, what was the rationale for not allowing this?

Detail:

So I have become the owner of a class with a method that is returning the std::map from Hell, and I really don't want to include hell.h everywhere /my/ class is used, just in the few places that really want to go there.

So my thought was to change the return signature of the method from

#include "hell.h"
std::map<std::string, Hell> CopyTheObjectSpecified(...);

To

class Hell;
std::unique_ptr<std::map<std::string, Hell>> CopyTheObjectSpecified(...);

And, for the cost of a new and delete, I now don't need to detail Hell, except when the method is called, and that code will be safely scoped by unique_ptr. Given the map copy has lots of allocations, I'm not concerned about one more.

(ben-v suggests I'm erecting a compile firewall around Hell - good way to look at it)

However, one of the callers is doing:

void Angel(std::map<std::string, Hell>& hell)
{
  hell = CopyTheObjectSpecified(...);
}

Which used to reduce to a move assignment (or even a complete return copy elision - probably not)

So now I'm doing one of

void Angel(std::map<std::string, Hell>& hell)
{
  hell = * CopyTheObjectSpecified(...);
}

Or the more verbose, visually offensive, but trackable, and injectable almost anywhere:

void Angel(std::map<std::string, Hell>& hell)
{
  hell = CopyTheObjectSpecified(...) .get()[0] ;
}

It appears to me, looking at the debugs, that the overloads of * and get() don't seem to behold that the unique_ptr is an r-val, and that the content should therefore also be returned as an r-val.

(Note that my intention here is that the rval unique_ptr instance continues to own the object it is pointing to, and will delete it when it goes out of scope as normal, but that object will stripped of its contents by the move.)

So this is invoking copy/delete semantics instead of move semantics, which is an overhead. I can't give the code back running slower!

Of course I could simply add a std::move(), but that would be yet an extra smell when the original author gets it back. I still see "unencapsulated" std::move as smell. Perhaps eventually I will get used to seeing it.

void Angel(std::map<std::string, Hell>& hell)
{
  hell = std::move( * CopyTheObjectSpecified(...) ) ;
}

Or use the swap operation (thanks Ben Voigt)

void Angel(std::map<std::string, Hell>& hell)
{
  hell.swap ( * CopyTheObjectSpecified(...) ) ;
}

(And yes, the best (for now) implementation for CopyTheObjectSpecified() is to actually make and return a copy, rather than return some shared_ptr const reference to the ever-changing data)

Or I can minimally encapsulate the movability in a wrapper class for unique_ptr. Are there dangers in having a when-rval-content-movable unique_ptr?

Currently we code to c 11. Perhaps I need the help of the later standards to do this properly? But that may help me formulate a solution that is readable and transferable.

Grr! I just want this method /out/ of /my/ class! But that's not an option today.

CodePudding user response:

You say you have

#include "hell.h"
std::map<std::string, Hell> CopyTheObjectSpecified(...);

and

void Angel(Hell& hell)
{
  hell = CopyTheObjectSpecified(...);
}

but that doesn't make any sense (and wont compile -- you can't assign a map<string, Hell> to a Hell)

So what do you actually have that will compile and what are you actually trying to do that isn't working?

At the simplest level, it would seem there's no need for unique_ptr as std::map can be instantiated on an incomplete type just fine. So you can do:

class Hell;
std::map<std::string, Hell> CopyTheObjectSpecified(...);

and you only need the #include for code that wants to put something into the map or get it out of the map (or copy/duplicate the entire map). std::map already contains a level of indirection that means it can be moved without knowing the details of the key/value types.

CodePudding user response:

I'm not sure if my answer would help or not, but as far as I know that creating a unique_ptr means that the ownership of an object is can't be changed which means it's only valid in the scope where it has been defined/created so I think you can't move the content of it since it's going to be an ownership change.

  • Related