Do stl containers use implicit sharing? - c++

Its known that Qt widgets use implicit sharing. So I am interested if stl containers std::vector, std::string use implicit sharing too.
If no, why? Since it is very useful.
And if the answer is yes, how we can ascertain in it? I need simple C++ stl program which shows that stl containers use implicit sharing. It doesn't do deep copy when is copied.

No. They cannot. When you try to modify the contents of the container, or even calling a mutable begin() on it, it would imply a potential copy-on-write and thus invalidate all references and iterators to the container. This would be a hard to debug situation, and it is prohibited.
Although std::string is technically not a container, it is still prohibited to do copy-on-write since C++11:
References, pointers, and iterators referring to the elements of a basic_string sequence may be invalidated by the following uses of that basic_string object:
...
— Calling non-const member functions, except operator[], at, front, back, begin, rbegin, end, and rend.
[string.require]
... Since it is very useful.
Heh, what for? Passing by reference almost always solves all 'performance problems'. Atomic ref-counts are inherently non-scalable on multi-processors machines.

Aside from the objections raised by others to CoW behaviour in containers, here are a few more. These all fall into the category of behaviour that defies convention, and will therefore cause bizarre bugs from unsuspecting developers.
Exceptions
Allowing CoW would means that innocuous mutation operations on a container can fail with exceptions when they wouldn't otherwise. This would be a particular hazard with operator[] on either a std::vector or std::string
Threading
One might reasonable expect to be able to copy construct a container with the express purpose of handing it off to another thread without worrying about concurrency thereafter. Not so with CoW.

As it's noticed in similar question:
The C++ standard doesn't prohibit or mandate copy-on-write or any
other implementation details for std::string. So long as the semantics
and complexity requirements are met an implementation may choose
whatever implementation strategy it likes.
I think, same is true for std::vector
Also, you may be interested in this topic: How is std::string implemented

STL containers do not use implicit sharing. They always have plain value semantics.
The reason is runtime performance: In multithreaded programs (potentially but not necessarily running on multicore hosts) the locking overhead of the housekeeping data (e.g. reference counting, locking while copying before writing) by far outweighs the overhead of a plain value copy, which has no special threading implications at all. It is expected that programs which would suffer from copying around huge std::maps implement explicit sharing to avoid the copying.
In fact in the very early days of STL std::string did use implicit sharing. But it was dropped when the first multicore CPUs came up.

Related

Allocator Aware Container and propagate_on_container_swap

The std::allocator_traits template defines a few constants, like propagate_on_container_copy/move_assign to let other containers know whether they should copy the allocator of a second container during a copy or move operation.
We also have propagate_on_container_swap, which specifies whether the allocator should be copied during a swap operation.
Is it really necessary for an Allocator Aware container to check for allocator_traits<A>::propagate_on_container_swap in Container::swap()? Usually, I implement swap as follows:
Container::swap(Container& other)
{
Container tmp(std::move(other));
other = std::move(*this);
*this = std::move(tmp);
}
In other words, I simply implement swap in terms of move assignment. Since the move assignment operation already has to deal with allocator awareness (by checking propagate_on_container_move_assign), is it okay to implement Container::swap() like this, instead of writing a totally different swap function which explicitly checks for propagate_on_container_swap?
It is important to draw a distinction between requirements the standard places on your code vs the requirements it places on types provided by the implementor of the std::lib.
The container requirements specify how the std::containers must behave. However you are free to write your container however you like. Unless you input your container into std::code which requires the std::container behavior, you are good to go. There are just a couple of such places. For example if you adapt your Container with std::stack, then you will have to provide standard behavior if you expect the std::stack to behave according to the standard.
Getting back to your question, if you desire your Container to have the same behavior as one of the std::containers in this regard, then you will have to check and abide by all of the propagate_on traits, and all of the other allocator requirements. This is a non-trivial task. And I am not necessarily recommending it.
The std::containers will not perform a container move construction nor move assignment during swap. They will instead swap their internal representations. They will decide (at compile-time) based on propagate_on_container_swap whether or not they will swap allocators.
If propagate_on_container_swap is true, they will swap allocators and internal representations. Also in this case, swap will be noexcept in C++1z (we hope that is C++17) and forward.
If propagate_on_container_swap is false the allocators shall not be swapped, and need not even be Swappable. However the container internals are still swapped. In this case, if the two allocators do not compare equal, the behavior is undefined.
If you keep your Container::swap as it is, and create a std::stack based on your Container, the std::stack::swap will not have standard behavior. However, it will have the behavior of your Container::swap, and if that is fine with the client of said std::stack and whatever allocator they may be using, then no harm done. std::stack::swap will not behave in mysterious ways just because you didn't rigorously follow all of the intricate details for allocators that the std::containers are required to.
Sure, you can implement it using move-ctor and move-assignment of the container.
That's the way std::swap does it though, and there is absolutely no reason to re-write it instead of using the standard one.
Those propagate-constants allow omitting useless extra-work, and you are free t ignore the potential for optimization.
Just be aware that you are potentially doing too much work, aka your swap is potentially less efficient.

C++ Containers and right hand side

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.

Different efficiency of iterator and const_iterator (STL)

In Qt there are similar classes to list an map. These classes provide a begin_const() method that returns a const_iterator. The documentation says that these const_iterators should be used whenever possible since they are faster.
The STL only gives you a const_iterator if the instance itself is const. Only one begin() method is implemented (overloaded for const).
Is there any difference when read-accessing elements with iterator and const_iterator? (I dont'w know why there's a difference for them in Qt)
The documentation says that these const_iterators should be used whenever possible since they are faster.
It sure does. From http://qt-project.org/doc/qt-4.8/containers.html#stl-style-iterators:
For each container class, there are two STL-style iterator types: one that provides read-only access and one that provides read-write access. Read-only iterators should be used wherever possible because they are faster than read-write iterators.
What a stupid thing to say.
Safer? Yes. Faster? Even if this were the case (it apparently isn't with gcc and clang), it is rarely a reason to prefer const iterators over non-const ones. This is premature optimization. The reason to prefer const iterators over non-const ones is safety. If you don't need the pointed-to contents to be modified, use a const iterator. Think of what some maintenance programmer will do to your code.
As far as begin versus cbegin is concerned, that's a C++11 addition. This allows the auto keyword to use a const iterator, even in a non-const setting.
The best reason to use const is to avoid bugs and make the intent of the code more clear.
It's conceivable that, in some cases, the compiler could perform some optimizations that would not be possible with a non-const iterator. Aliasing (when multiple variables and parameters may reference the same object) is often an inhibitor of some optimizations. If the compiler could rule out some forms of aliasing by noting that the const-iterator can never change the value, then perhaps it would enable some optimizations.
On the other hand, I'd expect a compiler that's good enough to use constness in that way to be able to reach the same conclusion with flow analysis.

Smart pointers in container like std::vector?

I am learning about smart pointers (std::auto_ptr) and just read here and here that smart pointers (std::auto_ptr) should not be put in containers (i.e. std::vector) because even most compilers won't complain and it might seem correct. There is no rule that says smart pointers won't be copied internally (by vector class for example) and transfer its ownership, then the pointer will become NULL. In the end, everything will be screwed up.
In reality, how often does this happen?
Sometimes I have vectors of pointers and if in the future I decide I want to have a vector of smart pointers what would my options?
I am aware of C++0x and Boost libraries, but for now, I would prefer to stick to a STL approach.
Yes, you really can't use std::auto_ptr with standard containers. std::auto_ptr copies aren't equivalent, and because standard containers (and algorithms) are allowed to copy their elements at will this screws things up. That is, the operation of copying a std::auto_ptr has a meaning other than a mere copy of an object: it means transferring an ownership.
Your options are:
Use the Boost Smart Pointers library. This is arguably your best option.
Use primitive pointers. This is fast and safe, so long as you manage the pointers properly. At times this can be complex or difficult. For example, you'll have to cope with (avoid) double-delete issues on your own.
Use your own reference-counting smart pointer. That'd be silly; use a Boost Smart Pointer.
The problem you are referring to concerns auto_ptr since it moves ownership on copy. shared_ptr and unique_ptr work just fine with containers.
Any type that you use with a standard container template must conform with the requirements for that container. In particular, the type must satisfy the requirements for CopyConstructible and Assignable types.
Many smart pointers do satisfy these requirements and may be used with standard containers but std::auto_ptr is not one of them because copies of std::auto_ptr are not equivalent to the source that they were created or assigned from.
Although some implementations of standard container may work with auto_ptr in some situations it is dangerous to rely on such implementation details.
theoretically you can use std::auto_ptr with STL containers if you completely understand their internal implementation and don't do anything that can lose auto_ptr ownership, but practically it's much more safe to use containers with raw ptrs.
"In reality, how often does this happen?" - is very dangerous question by itself. first of all, STL - it's not a single standard implementation, there're many. Each one can implement containers in different ways, so your highly tuned code to avoid all "auto_ptr in containers" mines can burst switching to another STL implementation. Also, your code maintenance will be highly complicated, any correctly looking change to your code can break your program. how can you remember that or force others maintainers to remember? putting warnings in any place where such changes can occur? impossible.
so, conclusion: it's just a bad idea that brings nothing but headache
For classes that have an auto ptr data member, I always have a clone method that returns a new auto ptr. I then implement an assignment method and copy constructor that call the clone method (and never the default assignment operator of auto ptr). This way you can safely use the class in STL containers.

Is it a good idea to create an STL iterator which is noncopyable?

Most of the time, STL iterators are CopyConstructable, because several STL algorithms require this to improve performance, such as std::sort.
However, I've been working on a pet project to wrap the FindXFile API (previously asked about), but the problem is it's impossible to implement a copyable iterator around this API. A find handle cannot be duplicated by any means -- DuplicateHandle specifically forbids passing these types of handles to it. And if you just maintain a reference count to the find handle, then a single increment by any copy results in an increment of all copies -- clearly that is not what a copy constructed iterator is supposed to do.
Since I can't satisfy the traditional copy constructible requirement for iterators here, is it even worth trying to create an "STL style" iterator? On one hand, creating some other enumeration method is going to not fall into normal STL conventions, but on the other, following STL conventions are going to confuse users of this iterator if they try to CopyConstruct it later.
Which is the lesser of two evils?
An input iterator which is not a forward iterator is copyable, but you can only "use" one of the copies: incrementing any of them invalidates the others (dereferencing one of them does not invalidate the others). This allows it to be passed to algorithms, but the algorithm must complete with a single pass. You can tell which algorithms are OK by checking their requirements - for example copy requires only an InputIterator, whereas adjacent_find requires a ForwardIterator (first one I found).
It sounds to me as though this describes your situation. Just copy the handle (or something which refcounts the handle), without duplicating it.
The user has to understand that it's only an InputIterator, but in practice this isn't a big deal. istream_iterator is the same, and for the same reason.
With the benefit of C++11 hindsight, it would almost have made sense to require InputIterators to be movable but not to require them to be copyable, since duplicates have limited use anyway. But that's "limited use", not "no use", and anyway it's too late now to remove functionality from InputIterator, considering how much code relies on the existing definition.