Cannot add a unique_ptr to a std::array - c++

After searching through books and online until the onset of a big pain between my ears, I cannot figure out how to add a std::unique_ptr to a std::array.
The following is a class member:
std::array<std::unique_ptr<Media>, MAX_ELMS_NUM> m_collection;
In the .cpp file I am trying to add a new media pointer stuffed in a std::unique_ptr to the array:
Media* newMedia = CreateNewMedia(Media info stuff);
unique_ptr<Media> np(newMedia);
m_collection[0] = np;
Everything compiles except for the last line.

The last line is an attempt to do a copy assignment operation, which is deleted for std::unique_ptr. You need to use the move-assignment operator:
m_collection[0] = std::move(np);

You can't copy a unique_ptr, full stop. That's what "unique" means - there is only one unique_ptr pointing to the same object.
Now, you can assign a unique_ptr to a different one, but that clears the original one. Since it would be really confusing if a = b modified b, you have to specifically indicate that you want this, with std::move:
m_collection[0] = std::move(np);
After this line, m_collection[0] will point to the Media object, and np will be cleared.

std::unique_ptr can't be copied, but moved, because it's unique. You could use std::move.
The class (std::unique_ptr) satisfies the requirements of MoveConstructible and MoveAssignable, but not the requirements of either CopyConstructible or CopyAssignable.
std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object.
Media* newMedia = CreateNewMedia(Media info stuff);
unique_ptr<Media> np(newMedia);
m_collection[0] = std::move(np);
~~~~~~~~~
BTW: As #M.M mentioned, using std::unique_ptr::reset could solve your problem too, and more clear by avoiding all the temporary variables.
m_collection[0].reset(CreateNewMedia(Media info stuff));

Related

C++ new operator with std::move to "copy" pointer

I recently find a code snippet as follows:
// To be specific: the "Item" can be viewed as "std::pair<xxx, xxx>*" here
void moveItemDuringRehash(Item* itemAddr, Item& src) {
// This is basically *itemAddr = src; src = nullptr, but allowing
// for fancy pointers.
// TODO(T31574848): clean up assume-s used to optimize placement new
assume(itemAddr != nullptr);
new (itemAddr) Item{std::move(src)};
src = nullptr;
src.~Item();
}
The code is originated from the Folly Lib of Facebook. The functionality of this code is simple: copy std::pair* referenced by src to the memory pointed by itemAddr.
The implementation should be very simple, as mentioned in the comment. But actually, the code does not. The new operator with std::move is confusing, and I am not sure what is happening under the hood. I guess. Item{std::move(src)} construct a temp object with move ctor of std::pair*. And the temp object is copy to the object pointed by itemAddr by copy ctor of std::pair*. I am not sure if my guess is correct. Thank you for sharing your opinion. By the way, I was wondering if there is any performance benefit from this new operator with std::move.
Another question is why src.~Item() is needed? For safety, I need to set src (std::pair*) to nullptr. But why I need to use src.~Item() to dtor a nullptr?
My guess without much context:
The function implements a destructive move. It not only moves the value of the Item, but also destroys the Item object.
Similarly, it doesn't expect that there is already a Item object at the location itemAddr. Instead it constructs one.
For a scalar type like std::pair<xxx, xxx>*, there isn't really any difference between a destructive and a non-destructive move, but the function is written so that Item may also be a more complex type than a raw pointer. Types that are not actually raw pointers, but behave like them and can be used to replace raw pointers in allocators are called fancy pointers. The function is written in such a way that general allocators can be supported, including those with fancy pointers. (This isn't obvious from your shown code where no allocator type is mentioned, but that's how it looks in context of the Folly sources.)
A fancy pointer might have non-trivial construction and destruction, so that the destructive move operation and construction of a new object may behave differently than simple assignment.
new (itemAddr) Item{std::move(src)}; is a placement-new to construct a new Item object at the location itemAddr, move-constructed form src. Move-construction is a non-destructive move, so to destruct the source object that was moved from, src.~Item(); is still required.
I am not really sure why src = nullptr; is there. The user of the function may not access src after the destructor call either way. Since C++20 this is also true for the pseudo-destructor call of scalar types. For a fancy pointer the assignment may have side effects, but I don't see why that would matter here. The only reason I could think of is that this is used defensively.
There are no temporary objects involved here.
Please note that I am not familiar with the context in the library. This is just a guess based on what you showed and a quick look at the source file.

Insert object into std::deque that does not allow copy constructor

Im using a std::deque to hold some objects, and it works great as long as I can add new elements with deque.emplace_front. However, now I want to replace an element of the deque with an already existing object. When I try to do the following
auto it = mydeque.begin();
++it;
mydeque.insert(it, object);
mydeque.erase(it);
I get an error because my object does not allow copying. How can I get around this issue?
EDIT:
My object is of type hp::DoFHandler<dim>, documented here: https://www.dealii.org/current/doxygen/deal.II/classhp_1_1DoFHandler.html.
The reason I can not use emplace is because this method constructs a new object, while I want to insert my existing one.
I could create a minimal working example, but since Im using the FEM framework https://www.dealii.org/, it would require either installing it or downloading a Docker image.
The reason I can not use emplace is because this method constructs a new object, while I want to insert my existing one.
The element of a container is always a distinct object. If you insert an existing object, then the object must be copied or moved.
I get an error because my object does not allow copying. How can I get around this issue?
If the type allows moving, then you can use std::move. Othewise, you cannot insert a pre-existing object into the container.
Some XY-solutions for non-movable types:
Avoid the problem by creating the object within the container initially, using emplace.
Use indirection. Instead of storing hp::DoFHandler<dim> objects in the container, rather store something that refers to such object like a pointer for example. This approach has the drawback of having to ensure that the lifetime of the pointer doesn't exceed the lifetime of the pointed object. Using shared ownership (std::shared_ptr) is an easy way to make sure of that, but it has other potential drawbacks.

C++: Moving to unique_ptr

My problem is I'm frontend developer, but I need to add changes to C++ project.
So how it was:
std::vector<myObject*> myArray;
...
myArray.push_back(new myObject);
...
myArray.back();
How I try to change it:
std::vector<std::unique_ptr<myObject>> myArray;
...
myArray.push_back(std::unique_ptr<myObject> (new myObject));
...
myArray.back().get(); // this place looks not good in my opinion :)
A lot of functions should get myObject*. And I don't know how many changes will be if I change parameters of function.
Could you advise how to get the pointer correctly in this case?
As mentioned in the comments, you should use
myArray.push_back(std::make_unique<myObject>());
(or emplace_back instead of push_back) rather than new. Direct use of new should be avoided wherever possible.
You shouldn't have to call .get() on a std::unqiue_ptr that often.
You can obtain a reference to the stored object with *:
auto& reference_to_object = *myArray.back();
You can access members directly through ->:
myArray.back()->some_function();
Only if you need a non-owning pointer (which should be much rarer than a reference) do you need to call .get():
auto ptr_to_object = myArray.back().get();
or you can use
auto ptr_to_object = &*myArray.back();
for that (except in the very unlikely case that operator& is overloaded for myObject).
I think the issue is that as you say "A lot of functions should get myObject*". This should be much rarer than you make it sound. Only functions that would also accept a null pointer as argument should take pointers as parameter. Otherwise references should be preferred. The former doesn't happen as frequently.
Also note that you really should have a good reason to use (smart) pointers at all inside the std::vector. There are few reasons not to just use std::vector<myObject>, such as myObject not being movable, being very expensive to move, myObject being used polymorphically or references to the contained objects being required to not be invalidated when elements are added/removed. (Although std::list might work better in the latter case.) If none of these, or other reasons I forgot to mention, apply, just use std::vector<myObject>.

What are the benefits and risks, if any, of using std::move with std::shared_ptr

I am in the process of learning C++11 features and as part of that I am diving head first into the world of unique_ptr and shared_ptr.
When I started, I wrote some code that used unique_ptr exclusively, and as such when I was passing my variables around I needed to accomplish that with std::move (or so I was made to understand).
I realized after some effort that I really needed shared_ptr instead for what I was doing. A quick find/replace later and my pointers were switched over to shared but I lazily just left the move() calls in.
To my surprise, not only did this compile, but it behaved perfectly well in my program and I got every ounce of functionality I was expecting... particularly, I was able to "move" a shared_ptr from ObjectA to ObjectB, and both objects had access to it and could manipulate it. Fantastic.
This raised the question for me though... is the move() call actually doing anything at all now that I am on shared_ptr? And if so, what, and what are the ramifications of it?
Code Example
shared_ptr<Label> lblLevel(new Label());
//levelTest is shared_ptr<Label> declared in the interface of my class, undefined to this point
levelTest = lblLevel;
//Configure my label with some redacted code
//Pass the label off to a container which stores the shared_ptr in an std::list
//That std::list is iterated through in the render phase, rendering text to screen
this->guiView.AddSubview(move(lblLevel));
At this point, I can make important changes to levelTest like changing the text, and those changes are reflected on screen.
This to me makes it appear as though both levelTest and the shared_ptr in the list are the same pointer, and move() really hasn't done much. This is my amateur interpretation. Looking for insight. Using MinGW on Windows.
ecatmur's answer explains the why of things behaving as you're seeing in a general sense.
Specifically to your case, levelTest is a copy of lblTest which creates an additional owning reference to the shared resource. You moved from lblTest so levelTest is completely unaffected and its ownership of the resource stays intact.
If you looked at lblTest I'm sure you'd see that it's been set to an empty value. Because you made a copy of the shared_ptr before you moved from it, both of the existing live instances of the pointer (levelTest and the value in guiView) should reference the same underlying pointer (their get method returns the same value) and there should be at least two references (their use_count method should return 2, or more if you made additional copies).
The whole point of shared_ptr is to enable things like you're seeing while still allowing automatic cleanup of resources when all the shared_ptr instances are destructed.
When you move-construct or move-assign from a shared pointer of convertible type, the source pointer becomes empty, per 20.7.2.2.1:
22 - Postconditions: *this shall contain the old value of r. r shall be empty. r.get() == 0.
So if you are observing that the source pointer is still valid after a move-construct or move-assignment, then either your compiler is incorrect or you are using std::move incorrectly.
For example:
std::shared_ptr<int> p = std::make_shared<int>(5);
std::shared_ptr<int> q = std::move(p);
assert(p.get() == nullptr);
If you copy a shared_ptr, the reference count to the pointer target is incremented (in a thread-safe way).
Instead, when you move a shared_ptr from A to B, B contains the copy of the state of A before the move, and A is empty. There was no thread-safe reference count increment/decrement, but some very simple and inexpensive pointer exchange between the internal bits of A and B.
You can think of a move as an efficient way of "stealing resources" from the source of the move to the destination of the move.

Problems with boost::ptr_vector and boost::any

ok, so I got a doubt, I want to know if this is possible:
I'm using a database, with generic data (strings, ints, bools, etc...). Whenever an object is constructed or a member of an object is modified, I have to query the database with the specific action (SELECT or UPDATE).
First of all, this isn't a DB related question, my real problem is that I have a ptr_vector which holds boost::any's pointers to members of the object. In code something like this:
class Enemy{
private:
//some private data...
public:
auto_ptr<int> ID_Enemy;
auto_ptr<string> Enemy_Name;
//miscellaneous methods...
};
then I pass the members I want to modify to a function of another miscellaneous class which takes as argument a boost::any*:
misc_class.addValues((boost::any*)(ID_Enemy.get()));
misc_class.addValues((boost::any*)(Enemy_Name.get()));
that same class accepts the any*, and does the following:
auto_ptr<boost::any> val2(val); //being val, the passed any*
Enemy_Values.push_back(val2);
Enemy_Values is a ptr_vector. So when I access this misc_class which has Enemy_Values as member, I want to change the value to which an auto_ptr inside is pointing:
misc_class.Enemy_Values[0] = (boost::any)(69);
And here, I get a violation error. I've tried many things, and someone told me that I shouldn't be using containers of auto_ptr or converting back and forth with boost::any. Is this that I am doing possible, or there is a better and more intuitive way?
Thanks in advance.
(boost::any*)(ID_Enemy.get()) performs a reinterpret_cast since you are casting unrelated pointer types. This means you get an invalid pointer to an any, pointing to what is really an integer.
Instead, construct a temporary boost::any object and pass it by reference to addValues:
misc_class.addValues(boost::any(ID_Enemy.get());
Your use of auto_ptr is in fact incorrect: auto_ptr deletes objects on the freestore but here we're dealing with locals instead. addValues merely needs to push the value of the any object into the vector:
Enemy_Values.push_back(val);
... and Enemy_Values should just be a std::vector.
You could do this with a ptr_vector and freestore-allocated boost::any objects, but that would be more complicated than necessary.
auto_ptr has a number of problems. Since you are already using boost, why not use boost::shared_ptr instead?