I am aware that you have to be careful with auto pointers (and why), especially with the STL. But I don't see a problem with this:
std::map<T1, std::auto_ptr<T2> >;
Is this safe?
I see how it would break in an std::vector, because it has to copy its items from time to time, but is this also true for the value type of an std::map?
Edit: Apparently, regardless whether it's safe, I cannot (technically) populate the map, but I'll leave the question open for theoretical considerations. Otherwise, consider it closed.
It's not safe. Technically, std::auto_ptr doesn't meet the requirement of CopyConstructible or Assignable because copies made using auto_ptr's copy constructor or copy assignment operator aren't equivalent to the source of the copy after the copy operation. These requirements must be met for any type used with any standard container.
You may find that you appear to get the behaviour you expect on one implementation for one particular use case but if you violate the requirements of the container you can't expect your code to work in all situations.
Still not safe. If nothing else, the first time you retrieved the pointer from the map you would transfer ownership and make it invalid.
There is an extremely narrow valid use case for auto_ptr. The only one I can remember coming across is when an object wants to make it explicitly clear that it takes ownership and responsibility for a pointer that you hand it.
Related
I've got following code:
std::list some_data;
...
std::list new_data = std::move(some_data);
some_data.clear();
...
The question is whether some_data.clear() is necessary? (for the record, some_data will be reused in the future)
Yes, it's necessary.
Only the std smart pointers are guaranteed to be in a default constructed state after being moved from.
Containers are in an valid, but unspecified state. This means you can only call member functions without preconditions, e.g. clear, that put the object in a fully known state.
The working draft of standard (N4713) states about the state of objects after they are moved from:
20.5.5.15 Moved-from state of library types [lib.types.movedfrom]
1 Objects of types defined in the C++ standard library may be moved from. Move operations may be explicitly specified or implicitly generated. Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state.
20.3.25 [defns.valid]
valid but unspecified state value of an object that is not specified except that the object’s invariants are met and operations on the object behave as specified for its type
The only operations you can safely perform on a moved-from container are those that do not require a precondition. clear has no preconditions. And it will return the object to a known state from which it can be used again.
No it is not necessary to clear the list. It is just reasonable and convenient to do so.
The list will have N elements, each taking an unspecified value, for some N≥0. So if you want to re-poppulate the list, you may in theory assign to the elements that are kept rather than clear and re-insert everything from scratch.
Of course chances that N≠0 are vanishingly small, so in practice clearing is the right choice.
Short answer:
Yes, you should clear it, because the standard doesn't explicitly define in what state the source list is after the move.
Furthermore, always calling clear() when a container gets reused after being moved from is a simple rule that should not cause any significant overhead even when it is redundant.
Longer answer:
As I mentioned in some comments, I can't imagine any legal and sensible implementation for which the source std::list will be anything but an empty std::list after being used as the source for a move constructor (thanks #Lightness Races in Orbit for digging through the standard). Move constructing must happen in linear time, which doesn't allow any per-object operations and AFAIK it is also not allowed to have a small, fixed-size inplace buffer that could be a source of left-over "zombie" elements in the source list.
However, even if you can track down all of that in the standard, you'd still have to document that everytime you omit the clear() in the code and be wary of someone refactoring the code by replacing the std::list with a homegrown container, that doesn't quite full fill the requirements in the standard (e.g. to make use of the small buffer optimization). Also, the previous statement is only valid for move construction. On moveassignment it is absolutely possible that the moved from container will not be empty.
In summary: Even if it was technically correct to not use clear in this particular case, it is imho not worth the mental effort to do it (and if you are in an environment where it is worth it, you are probably tuning your code to a particular version of the standard library and not the standard document anyway).
It rather depends on what you mean by "reused". There are many reuses that totally define the state of the object, with no regard to it's previous state.
The obvious one is assigning to a moved-from object, which occurs in the naive swap
template <typename T>
void swap(T& lhs, T& rhs)
{
T temp = std::move(lhs);
lhs = std::move(rhs); // lhs is moved-from, but we don't care.
rhs = std::move(temp); // rhs is moved-from, but we don't care.
}
If you instead wish to "reuse" by calling some_data.push_back, then yes, you should clear it first
I was writing some code that has a generic container that requires elements to be nothrow_move_constructible.
I decided to add a static_assert that enforces this, just in case.
To my surprise I can't compile now when using boost::container::flat_set.
I assumed that this was just an oversight and I need a more recent boost verison, but it seems that actually they deliberately made it not safely movable:
See docs here:
http://www.boost.org/doc/libs/1_61_0/doc/html/boost/container/flat_set.html
You can see that they did update it to use R-value references and marked swap as noexcept, but they chose not to make the move ctor noexcept.
It appears that move assignment is conditionally noexcept. The condition appears to depend on the value type and on the allocator in some way.
What could be the rationale for not being nothrow move constructible? Is it just an oversight?
If the objects within a container are not nothrow_move_constructible then it is very dangerous to take an entire set of one container and relocate it to another under certain conditions (usually involving Allocators). If two containers were not constructed with the same allocator, then it is no longer safe to move memory from one container to another (think two containers from two different memory arenas).
Digging in to the current source, both the contract and the implementation are problematic:
//! <b>Effects</b>: Move constructs a flat_map.
//! Constructs *this using x's resources.
flat_map(BOOST_RV_REF(flat_map) x)
: m_flat_tree(boost::move(x.m_flat_tree))
{ ... }
So your current expectation that it could currently by nothrow is correct. But what they are doing is probably not right.
I can only guess that they are worried that they will have to do revisit this in the future and don't want to have to weaken the nothrow contract later.
I just stumbled into an issue with std::vector when adding new elements to it.
It seems when you try to add more elements to it, and it needs to allocate more space, it does so by copying last element all elements it currently holds. This seems to assume that any element in the vector is fully valid, and as such a copy will always succeed.
In our case, this is not necessarily true. Currently we might have some left over elements in the vector, because we chose not to remove them yet, which are valid objects, but their data doesn't guarantee valid behavior. The objects have guards, but I never considered adding guards to a copy constructor as I assumed we would never be copying an invalid object (which vector forces) :
CopiedClass::CopiedClass(const CopiedClass& other)
: member(other.member)
{
member->DoSomething();
}
It just so happened that "member" is nulled when we are done with the original object and leave it lying about in the vector, so when std::vector tries to copy it, it crashes.
Is it possible to prevent std::vector from copying that element? Or do we have to guard against possible invalid objects being copied? Ideally we'd like to keep assuming that only valid objects are created, but that seems to imply we immediately clean them from the vector, rather than waiting and doing it at some later stage.
It's actually a design flaw in your CopiedClass that needs to be fixed.
std::vector behaves as expected and documented.
From the little code you show
member->DoSomething();
this indicates you are going to take a shallow copy of a pointer to what ever.
in our case, this is not necessarily true. Currently we might have some left over elements in the vector, because we chose not to remove them yet, which are valid objects, but their data doesn't guarantee valid behavior.
It just so happened that "member" is nulled when we are done with the original object and leave it lying about in the vector, so when std::vector tries to copy it, it crashes.
As your copy constructor doesn't handle that situation correctly regarding following the Rule of Three your design of CopiedClass is wrong.
You should either care to create a deep copy of your member, or use a smart pointer (or a plain instance member), rather than a raw pointer.
The smart pointers should take care of the management for these members correctly.
Also for the code snippet from above, you should test against nullpointer before blindly dereferencing and calling DoSomething().
Is it possible to prevent std::vector from copying that element?
As you're asking for c++11 it's possible, but requires you never change the size of the vector, and provide a move constructor and assignment operator for CopiedClass.
Otherwise, std::vector explicitly requires copyable types (at least for certain operations):
T must meet the requirements of CopyAssignable and CopyConstructible.
It's your responsibility to meet these requirements correctly.
... it does so by copying the last element it currently holds.
Also note: In a situation the vector needs to be resized, all of the existing elements will be copied, not only the last one as you premise.
If you know the vector's maximum size in advance, then a workaround for this issue would be calling reserve with that maximum size. When the vector's capacity is big enough, no reallocations occur and no copies of existing elements need to be made.
But that's really just a workaround. You should redesign your class such that copying cannot suddenly become an invalid operation, either by making copying safe or by disallowing it and perhaps replace it with moving, like std::unique_ptr. If you make the validity of copying dependent on some run-time status, then you don't even need a std::vector to get into trouble. A simple CopiedClass get(); will be a potential error.
It seems when you try to add more elements to it, and it needs to
allocate more space, it does so by copying the last element it
currently holds. This seems to assume that any element in the vector
is fully valid, and as such a copy will always succeed.
Both sentences are not really correct. When vector runs out of space it, yes, performs reallocation, but of all the elements and these are copied to the new location or possibly moved. When you push_back or emplace_back, and reallocation is needed, the element will generally by copied: if an exception is thrown during that, it will be treated as if you had never added the element. If vector detects a noexcept user-defined move constructor ( MoveInsertable concept), elements are moved; if this constructor though is not noexcept, and an exception is thrown, the result is unspecified.
The objects have guards, but I never considered adding guards to a
copy constructor as I assumed we would never be copying an invalid
object (which vector forces) :
vector copies its value_type: it does not care whether what it contains is valid or invalid. You should take care of that in your copy control methods, whose scope is exactly to define how the object is passed around.
CopiedClass::CopiedClass(const CopiedClass& other)
: member(other.member)
{
member->DoSomething();
}
The obvious solution would be to check if member is valid and act thereby.
if (member.internalData.isValid())
member->DoSomething()
// acknowledge this.member's data is invalid
We don't know how member is represented, but Boost.Optional is what you may be looking for.
Is it possible to prevent std::vector from copying that element?
Reallocation is something vector is expected to commit so, no, you can't. reserv-ing space could avoid that, but maintaining code to handle that would not really be painless. Rather, prefer a container like std::forward_list/std::list.
Other solutions:
Hold a unique_ptr<decltype(member)>, but pointers are not often a real solution
Define move semantics for your object, as described in other answers.
I've read today that you should not use the STL containers for auto_ptr because of
the fact that the auto_ptr deletes it rhs value in the = operator.
So i have 2 question :
1) Does this mean that all classes that have this behavior should not be used in containers?
2) what sort of containers can you use?
1) Does this mean that all classes that have this behavior should not
be used in containers?
Yes indeed, because that is not correct copying behaviour, since the copy is not equal to the source afterwards but destroys the source. This is kind of a broken implementation of move-semantics before C++11, required for the strict unique ownership semantics of std::auto_ptr.
2) what sort of containers can you use?
The real answer is actually, that classes having this behaviour (a copy constructor/assignment destroying its source) should just not exist. And fortunately this is not needed anymore nowadays, since C++11 has proper move-semantics, which realize exactly this destructive copy but in a safe way (simply said, only when the source is really not needed anymore).
Therefore std::auto_ptr is deprecated and should not be used anymore. It has been replaced by std::unique_ptr which is movable but not copyable. But since C++11 containers rather move their elements than copy when appropriate, a std::unique_ptr can be used perfectly inside of standard containers. You just cannot copy the container or fill it with a single object which would require copies of std::unique_ptrs, but those operations should not work anyway, since they are conceptually wrong for unique ownership semantics.
And as a side note, if you actually chose std:auto_ptr for a reason, that is you want unique ownership semantics, then a std::shared_ptr (as suggested by other answers) is plain wrong since it exhibits shared ownership. std::unique_ptr is today's std::auto_ptr. Never spam std::shared_ptrs where std::unique_ptrs (or even raw pointers, but from your question I rule that option out) are appropriate.
Auto pointer has a very strict ownership: it and only it is responsible for the lifetime of the object it points at. If you copy an auto_ptr, you lose the reference to what it pointed at.
The problem is in the way STL containers do their stuff. For example, when you are adding an element, the container might expand to get more memory, which leads to copying all the values to the new memory, which, itself, leads to losing the auto_ptrs.
I think that associative containers might not copy themselves entirely when they allocate additional memory, but I'm absolutely not sure, if someone can confirm it, please post a comment, or just edit my answer. Anyway, you'd better not risk it.
Also note that Auto_ptr is deprecated since C++0x, it is advised to use unique_ptr instead. In your case, std::shared_ptr will probably do the trick, unless you really need unique ownership for those objects of yours.
Exactly.
In general, sequence container elements must be CopyConstructible and Assignable. This means that they require:
public copy constructor
public assignment operator
Asociative containers (set<> and map<>) also must provide strict weak ordering, i.e. operator < must be defined (or a dedicated comparison function).
Chapter 23.1 of C++ standard provides detailed requirements.
Regarding to the requirement for C++ stl container element, the standard says: the element type should be CopyConstructible, and there is a table for CopyConstructible requirements. Also by various books (Josuttis, etc.), the generated copy should be "equivalent to" the source.
I think I need some clarity here. What is exactly "equivalent to"? Also I am a bit confused with the relation between the "CopyConstructible" and the "deep/shallow copy". In general, a copy constructor is either shallow copy or deep copy. So which one applies to the "CopyConstructible", and which does not?
Thanks for any comments!
Deep or shallow copy both work. For instance, shared_ptr always does a shallow copy (with some extra reference counting stuff), and you can use them in containers just fine. It depends on the semantics of copy-operation.
Equivalent means your program should not depend on whether it works with the original or with the copy.
If you put something into a container, when you retrieve it you will get something that is equivalent to what you put in. So long as that is meaningful for your objects then you will get out something useful from the container.
Whether that is a shallow or deep copy depends on the semantics that you want for your object type. Your object might be pointer-like, handle-like or perhaps container like. It might contain some mutable cache data that you may or may not choose to duplicate on a copy operation.
So long as your copy constructor is accessible and does what you need it to do to preserve the semantics of your object type then you satisfy the CopyConstructible requirement.
In general, STL containers may copy your elements around at some stage, during some kinds of operations or algorithms, so the litmus test is:
Element original(....); // construct this awesome object
original.this_and_that(); // do stuff to it until the value is perfect...
Element another(original);
Could you use another happily instead of original?
That's effectively what the CopyConstructible requirement's saying: you better be able to have this copied into another object and still be happy with the result. It's not a draconian restriction - you just need to think it through and write your copy constructor correspondingly.
But, it's significant in that some operations like find() may use == to compare elements (for other containers, it may be '<'), so if a side-effect of being copied is that you can't compare elements meaningfully, then your finds et al may stop working - think that through too! (The Standard says for containers, "== is an equivalence relation" (23.1-5).)
Thinking that "a copy constructor performs either a deep or a shallow copy" is slightly limiting, and something of a red herring.
Whilst it's true that, depending on what your object stores as members, you may need to do some deep copying in order to gain equivalence, as far as the type's interface is concerned, it doesn't really matter how you performed the copy... as long as you did perform the copy and you ended up with an equivalent object.
And if A is equivalent to B, then for a properly-designed type, A==B.
The entire requirement is just saying: "the element type must be copyable". All the rest is down to usual common sense of writing a proper copy constructor.
There are a couple of different concepts being discussed here. CopyConstructible only requires that you can create a new element of that type using an existing element as the source from which to copy. It is unrelated to deep or shallow or even equivalence: the container does not care what you are doing while copying as long as it is allowed to perform that copy.
The second concept is the concept of equivalence, when you use an object in a container it will get copied, and the number of copies performed is unknown --the implementation might copy it just once to store it internally, or it might make multiple copies internally. What you want is to be able to extract the element from the container and use it as if it was the original object, here is where equivalence comes in: the n-th copy that you extract should be equivalent to the object that you inserted.
The concepts of deep and shallow copying are directly related to equivalence, depending on the domain that you are modeling, equivalence might require either shallow or deep copying, and depending on other constraints you might have to choose one or the other --as long as in your domain they are equivalent-- or there might even be intermediate options, where a partially deep copy is performed, or some members are not copied at all.
Related post: What is the difference between equality and equivalence?
Edited to make this a "non comment".
Equivalence means "equal for all intents and purposes". For example: 1.0001 and 1 are never equal, but under certain circumstances they are equivalent. This is the general answer.
What the books mean are that the copied objects must satisfy the strict weak ordering condition with the original object: copy < original == false && original < copy == false.