For allocators why is select_on_container_copy_construction needed opposed to just overloading the copy-constructor?
Are there instances when we want to define two seperate copy-construction implementations depending on if we are copying the actual allocator vs container?
(The trait you're referring to is actually called select_on_container_copy_construction.)
The copy constructors of standard library containers are in fact overloaded and provide an allocator-extended version:
A a1 = f(), a2 = g(); // allocators
std::vector<int, A> v1(a1);
std::vector<int, A> v2(v1, a2); // allocator-extended copy
std::vector<int, A> v3 = v1; // regular copy, uses select_on_container_copy_construction
However, using an overload is not always an option, and generally allocator-aware containers should be usable just as easily and seamlessly as if you weren't aware of the allocator choices. That means that certain decisions, such as how to allocate a copy of a container, may need to be customizable directly via the allocator type, and not via the user's type.
For example, you could imagine a situation where the contents of one vector all go onto one (possibly growable) arena, but when you make a new vector, you'd like that to go onto a new, separate arena, and generic code should not need to have to know about this.
Whether this library feature is useful in practice is a separate question, but hopefully this shows why this part design has some motivation.
Related
Here is the documentation on cppreference, here is the working draft.
I must admit that I didn't understand what's the real purpose of polymorphic_allocator and when/why/how I should use it.
As an example, the pmr::vector has the following signature:
namespace pmr {
template <class T>
using vector = std::vector<T, polymorphic_allocator<T>>;
}
What does the polymorphic_allocator offer? What does the std::pmr::vector offer as well in regard of the old-fashioned std::vector?
What can I do now that I wasn't able to do till now?
What's the real purpose of that allocator and when should I use it actually?
Choice quote from cppreference:
This runtime polymorphism allows objects using polymorphic_allocator to behave as if they used different allocator types at run time despite the identical static allocator type
The issue with "regular" allocators is that they change the type of the container. If you want a vector with a specific allocator, you can make use of the Allocator template parameter:
auto my_vector = std::vector<int,my_allocator>();
The problem now is that this vector is not the same type as a vector with a different allocator. You can't pass it to a function which requires a default-allocator vector, for example, or assign two vectors with a different allocator type to the same variable / pointer, eg:
auto my_vector = std::vector<int,my_allocator>();
auto my_vector2 = std::vector<int,other_allocator>();
auto vec = my_vector; // ok
vec = my_vector2; // error
A polymorphic allocator is a single allocator type with a member that can define the allocator behaviour via dynamic dispatch rather than through the template mechanism. This allows you to have containers which use specific, customised allocation, but which are still of a common type.
The customisation of allocator behavior is done by giving the allocator a std::memory_resource *:
// define allocation behaviour via a custom "memory_resource"
class my_memory_resource : public std::pmr::memory_resource { ... };
my_memory_resource mem_res;
auto my_vector = std::pmr::vector<int>(0, &mem_res);
// define a second memory resource
class other_memory_resource : public std::pmr::memory_resource { ... };
other_memory_resource mem_res_other;
auto my_other_vector = std::pmr::vector<int>(0, &mes_res_other);
auto vec = my_vector; // type is std::pmr::vector<int>
vec = my_other_vector; // this is ok -
// my_vector and my_other_vector have same type
The main remaining issue, as I see it, is that a std::pmr:: container is still not compatible with the equivalent std:: container using the default allocator. You need to make some decisions at the time you design an interface which works with a container:
is it likely that the container passed in may require custom allocation?
if so, should I add a template parameter (to allow for arbitrary allocators) or should I mandate the use of a polymorphic allocator?
A template solution allows for any allocator, including a polymorphic allocator, but has other drawbacks (generated code size, compile time, code must be exposed in header file, potential for further "type contamination" which keeps pushing the problem outward). A polymorphic allocator solution on the other hand dictates that a polymorphic allocator must be used. This precludes using std:: containers which use the default allocator, and might have implications for interfacing with legacy code.
Compared to a regular allocator, a polymorphic allocator does have some minor costs, such as the storage overhead of the memory_resource pointer (which is most likely negligible) and the cost of virtual function dispatch for allocations. The main problem, really, is probably lack of compatibility with legacy code which doesn't use polymorphic allocators.
polymorphic_allocator is to a custom allocator as std::function is to a direct function call.
It simply lets you use an allocator with your container without having to decide, at the point of declaration, which one. So if you have a situation where more than one allocator would be appropriate, you can use polymorphic_allocator.
Maybe you want to hide which allocator is used to simplify your interface, or maybe you want to be able to swap it out for different runtime cases.
First you need code that needs an allocator, then you need to want to be able to swap which one is used, before considering pmr vector.
One drawback of polymorphic allocators is that polymorphic_allocator<T>::pointer is always just T*. That means you can't use them with fancy pointers. If you want to do something like place elements of a vector in shared memory and access them through boost::interprocess::offset_ptrs, you need to use a regular old non-polymorphic allocator for that.
So, although polymorphic allocators let you vary allocation behavior without changing a container's static type, they limit what an allocation is.
What's the difference between supplying an STL container (for example, std::vector) with an allocator as a template parameter, eg.:
std::vector<int, std::allocator<int>> some_ints;
and supplying an allocator as a constructor argument, eg:
std::allocator<int> temp;
std::vector<int> some_ints(temp);
and what are the advantages of either, given that they are not the same thing (ie. one supplies a type, the other a type instance) and can be used separately from each other?
Can be used separately from each other?
The template parameter just supplies the type. You still need an instance. It's not separable.
It's like having a function template<typename Type> f(Type instance); and asking what is the difference between Type and instance, can they be used separately and what are the advantages of either. It does not make much sense if you do understand what is a template, type and an instance/object.
(for the sake of simplicity it's c++11)
Here you have type template for vector:
template<
class T,
class Allocator = std::allocator<T>
> class vector;
And here is the default constructor:
explicit vector( const Allocator& alloc = Allocator() );
There always is an instance of Allocator provided as alloc parameter. All other invocation are similar in this regard. By default it is default constructed new Allocator object. So, semantically, whenever you do not use invocation of vector specifying allocator parameter, you do create new Allocator object (which in default case most probably does nothing, but the logical flow of the program is as described).
You cannot pass something that would not fit Allocator because you would get type-mismatch, or precisely in this case a substitution failure.
One pretty non-standard you could do without touching the definition of vector is to define DerivedAllocator which derives from Allocator instantiate it and pass as an argument. So for example:
vector<T> v( DerivedAllocator<T>() );
But I am not able to come up with a use-case for such construction on the top of my head. There is a good use-case, see the addendum below.
What is the Allocator template parameter useful for?
In some system you have more than one type of memory, so it might be useful to provide separate allocators (presicely separate allocator types). E.g: SRamAllocator, RamAllocator, etc.
This is quite common in embedded systems. I know that somewhere there there is a memory model in implementation which actually does not free, when you free it it's a lost chunk. It's essentially a moving pointer. The rationale is that it's extremely fast because it does not have any logic to trace blocks of "holes" caused by freeing. You wouldn't want to use it scenarios with heavy new/delete patterns.
What is the allocator constructor parameter useful for?
It makes sense in case of stateful allocators. Imagine you want to have two storages of the same type. E.g. to track some memory usage, or whatever reason you come with to have more than one logical "memory banks". You may want to create an allocator for each thread in your program, so it's easier to maintain correct CPU/memory affinity.
When you create a new object, you need to tell which of the allocators instances should take care of it.
You could technically implement everything just using different type for each instance, but that would strip down the usability of possible run-time dynamism.
NOTE: Default allocator and pre-c++11 custom allocators are disallowed to have a state, so they basically that to be implemented in a fully static way. It actually does not matter instance of Allocator you use. That is why the default Allocator() works.
So, theoretically one would no need then to instantiate them, and could work with just type and a static interface... if the standard said so. But it was deliberately not made this way to allow allocator types with an internal state (this sentence is a personal opinion).
IMPORTANT ADDENDUM: I've missed one important perk of c'tor parameter allocator, which is quite possibly it's raison d'ĂȘtre. Polymorphic allocators. Is described in detail here: polymorphic_allocator: when and why should I use it?
Basically, using different Allocator type would change the whole type of the object, so one end's up with basically the same object which differ only by allocator. This is under certain circumstances highly undesirable. To avoid it, one can write a polymorphic allocators and use base allocator in the type, and concrete implementations as the runtime parameters. Therefore, one can have object of exactly the same type using different storage engines. So using parameter has some overhead, but it reduces status of the allocator from being iron branded onto the type, to more of an implementational detail.
They actually are exactly the same thing.
In the first example, the vector's default constructor default-constructs an allocator of the type you specified.
In the second, you provided the allocator yourself; it happens to match the default type for the container's allocator.
Both examples make use of default arguments; one is a default function argument, and the other is a default template argument. But the end result in each case is precisely the same.
Here's a demonstrative example:
// N.B. I've included one non-defaulted argument to match
// the vector example, but you could omit `T1` entirely and
// write e.g. `Foo<> obj4`.
template <typename T1, typename T2 = int>
struct Foo
{
Foo(T2 x = 42) : x(x) {}
private:
T2 x;
};
int main()
{
Foo<char, int> obj1; // like your first example
Foo<char> obj2(42); // like your second example
Foo<char> obj3; // this is more common
// obj1, obj2 and obj3 are not only of identical type,
// but also have identical state! You just got there
// in different ways.
}
I'm developing a container-like class and I would like to make use of the standard allocator infrastructure much like the standard containers. On the net I find a lot of material about how to use the std::allocator class alone, or how to define a custom allocator for standard containers, but the material about how to make generically use of an standard conforming allocator is very rare, in particular in the context of C++11, where things seem to be much easier from the point of view of who writes a custom allocator, but more complex from the container's point of view.
So my question is about how to correctly make use of a standard conforming allocator in the most generic way, specifically:
First of all, when should I design a custom container in this way? Is there a sensible performance overhead (including missing optimization opportunities) in using the default allocator instead of plain new/delete?
Do I have to explicitly call contained objects' destructors?
How do I discriminate between stateful and stateless allocators?
How to handle stateful allocators?
When (if ever) are two instances interchangeable (when can I destroy with one instance the memory allocated with another one)?
They have to be copied when the container is copied?
They can/have to be moved when the container is moved?
In the container's move constructor and move assignment operator, when can I move the pointer to allocated memory, and when do I have to allocate different memory and move the elements instead?
Are there issues about exception safety in this context?
I'm specifically interested in an answer about the C++11 world (does it change anything in C++14?)
In all answers below, I'm assuming you want to follow the rules for C++11 std-defined containers. The standard does not require you to write your custom containers this way.
First of all, when should I design a custom container in this way? Is there a sensible performance overhead (including missing
optimization opportunities) in using the default allocator instead of
plain new/delete?
One of the most common and effective uses for custom allocators is to have it allocate off of the stack, for performance reasons. If your custom container can not accept such an allocator, then your clients will not be able to perform such an optimization.
Do I have to explicitly call contained objects' destructors?
You have to explicitly call allocator_traits<allocator_type>::destroy(alloc, ptr), which in turn will either directly call the value_type's destructor, or will call the destroy member of the allocator_type.
How do I discriminate between stateful and stateless allocators?
I would not bother. Just assume the allocator is stateful.
How to handle stateful allocators?
Follow the rules laid out in C++11 very carefully. Especially those for allocator-aware containers specified in [container.requirements.general]. The rules are too numerous to list here. However I'm happy to answer specific questions on any of those rules. But step one is get a copy of the standard, and read it, at least the container requirements sections. I recommend the latest C++14 working draft for this purpose.
When (if ever) are two instances interchangeable (when can I destroy with one instance the memory allocated with another one)?
If two allocators compare equal, then either can deallocate pointers allocated from the other. Copies (either by copy construction or copy assignment) are required to compare equal.
They have to be copied when the container is copied?
Search the standard for propagate_on and select_on_container_copy_construction for the nitty gritty details. The nutshell answer is "it depends."
They can/have to be moved when the container is moved?
Have to be for move construction. Move assignment depends on propagate_on_container_move_assignment.
In the container's move constructor and move assignment operator, when can I move the pointer to allocated memory, and when do I have to allocate different memory
and move the elements instead?
The newly move constructed container should have gotten its allocator by move constructing the rhs's allocator. These two allocators are required to compare equal. So you can transfer memory ownership for all allocated memory for which your container has a valid state for that pointer being nullptr in the rhs.
The move assignment operator is arguably the most complicated: The behavior depends on propagate_on_container_move_assignment and whether or not the two allocators compare equal. A more complete description is below in my "allocator cheat sheet."
Are there issues about exception safety in this context?
Yes, tons. [allocator.requirements] lists the allocator requirements, which the container can depend on. This includes which operations can and can not throw.
You will also need to deal with the possibility that the allocator's pointer is not actually a value_type*. [allocator.requirements] is also the place to look for these details.
Good luck. This is not a beginner project. If you have more specific questions, post them to SO. To get started, go straight to the standard. I am not aware of any other authoritative source on the subject.
Here is a cheat-sheet I made for myself which describes allocator behavior, and the container's special members. It is written in English, not standard-eze. If you find any discrepancies between my cheat sheet, and the C++14 working draft, trust the working draft. One known discrepancy is that I've added noexcept specs in ways the standard has not.
Allocator behavior:
C() noexcept(is_nothrow_default_constructible<allocator_type>::value);
C(const C& c);
Gets allocator from alloc_traits::select_on_container_copy_construction(c).
C(const C& c, const allocator_type& a);
Gets allocator from a.
C(C&& c)
noexcept(is_nothrow_move_constructible<allocator_type>::value && ...);
Gets allocator from move(c.get_allocator()), transfers resources.
C(C&& c, const allocator_type& a);
Gets allocator from a. Transfers resources if a == c.get_allocator().
Move constructs from each c[i] if a != c.get_allocator().
C& operator=(const C& c);
If alloc_traits::propagate_on_container_copy_assignment::value is true,
copy assigns allocators. In this case, if allocators are not equal prior
to assignment, dumps all resources from *this.
C& operator=(C&& c)
noexcept(
allocator_type::propagate_on_container_move_assignment::value &&
is_nothrow_move_assignable<allocator_type>::value);
If alloc_traits::propagate_on_container_move_assignment::value is true,
dumps resources, move assigns allocators, and transfers resources from c.
If alloc_traits::propagate_on_container_move_assignment::value is false
and get_allocator() == c.get_allocator(), dumps resources, and transfers
resources from c.
If alloc_traits::propagate_on_container_move_assignment::value is false
and get_allocator() != c.get_allocator(), move assigns each c[i].
void swap(C& c)
noexcept(!allocator_type::propagate_on_container_swap::value ||
__is_nothrow_swappable<allocator_type>::value);
If alloc_traits::propagate_on_container_swap::value is true, swaps
allocators. Regardless, swaps resources. Undefined behavior if the
allocators are unequal and propagate_on_container_swap::value is false.
For example, why doesn't template< typename Elem, typename Traits, typename Alloc > basic_string { ... } provide:
template< typename OtherAlloc >
basic_string( const basic_string< Elem, Traits, OtherAlloc >& a_Other ) { ... }
It would seem fairly trivial to implement such a conversion constructor which respects both allocators. The current state of affair makes it very cumbersome to interface between types just differing in allocators.
The Standard hash also does not permit allocator fun- it can hash std::string and std::wstring but not std::basic_string<char, std::char_traits<char>, custom_alloc>. In addition, you cannot create an unordered_map or unordered_set with just an allocator- you must also provide a bucket number, which defaults to an implementation-defined constant you can't access, so you effectively must just make something up. The support, generally, is not very good.
It seems to me that, relatively simply, nobody proposed such a function or explored this use space.
The problem is much harder than it looks. Because the allocator type is part of the type of the object there is little interaction allowed between types that differ only in allocator. The first example that comes to mind is that a function that takes a std::string by constant reference cannot take a string that uses a different allocator. One particular such case is during construction of objects. As a matter of fact it is probably the harder case here.
Consider for example the following code:
// Assume that you could construct from a different allocator
std::vector<int, allocator1> f() {
std::vector<int, allocator2> r;
// fill in
return r;
}
int main() {
std::vector<int, allocator3> x = f();
}
Consider that allocator1 is std::allocator<int> (i.e. the default allocator), that allocator2 uses a local arena in the stack, and that allocator3 might use shared memory. In theory the code is quite simple, the vector r is created and filled with data, on the return statement a new temporary is created by copying from r, and finally x is constructed by copying from that temporary. The problem is that the standard allows (and compilers like) to avoid copying as much as possible. In the particular example above (and ignoring allocators) the compiler would elide both copies and create the buffer only once, which is fast and efficient. But with allocators potentially being different, NRVO and other types of copy-elision would have to be disabled. (If those optimizations are enabled, x in main would be using allocator2, with a local arena that has already been destroyed, causing undefined behavior).
By enabling copy construction from containers with one allocator into another you might end up in a mess, or in a deeper mess than we are already in the current standard, where you can cause all sort of interesting issues with stateful allocators (say that you use per-thread allocators, and that you move data into a shared queue, you might end up with one thread holding an object created by the per-thread allocator on another thread, and because the point of using per-thread allocators is avoid contentions on locks, you could be creating a race condition on apparently safe code....
This is an old proposal to the C++ committee Towards a better allocation model that raises some of the concerns with the C++03 allocation model and proposes a polymorphic allocator type (which has problems of it's own). It makes for an interesting read, but beware of the details, not everything is as good as it seems, and there are quite a few pitfalls in using either option (or the C++11 version that is similar to the C++03 version)
I'm currently learning about concurrency in C++ and came across using a vector of threads, which I believe will be possible in C++0x. However, my current compiler doesn't appear to have an implementation of move-aware containers and so I get errors generated because std::thread::thread(const std::thread&) is deleted, ie I can only use the move constructor/move assignment with std::thread.
Am I correct in thinking I could circumvent this issue by writing a custom allocator using
void MyAllocator::construct (pointer p, reference val)
/* should be non-const reference to val because using move constructor? */
{
new ((void*)p) T (std::move(val));
}
rather than
void allocator::construct (pointer p, const_reference val)
{
new ((void*)p) T (val);
}
? Or some other variation on this theme (possibly using an overload of MyAllocator::construct).
NB: This is mainly intended to be a short-term educational exercise and well enough performing work around to play around with threads in containers. I'd only be using MyAllocator in this context. However, please also point me at any libraries that may have this implemented so I can have a poke around the source.
If your compiler doesn't provide a move-aware std::vector then you'll have to write your own specialization of std::vector<std::thread> rather than just provide a custom allocator. The whole C++03 vector interface relies on copying: push_back() copies elements in; resize() initializes the empty elements with a copy of the element passed as the second parameter (even if that is the default value of T()); resize(), reserve(), insert(), erase() and push_back() will copy elements if the vector needs reallocating, or elements otherwise need moving around, and so forth.
This is such a common problem that I've included such a specialization with my (commercial) just::thread implementation of std::thread.
The easiest way to circumvent the problem would be to allocate the threads on the heap and manipulate pointers to them.
Check the Boost Pointer Container library: boost::ptr_vector<std::thread> seems to me what you are looking for.
The requirement that std containers only take copyable objects has more to do with the C++03 container interfaces than it does with the allocator implementation.
For example
vector<T> b(100);
vector<T> a;
a=b;
assert(a==b);
The standard assures us a==b is true. However, if T were not copyable, then in the best case a=b will not compile, in the worst a=b is undefined. Furthermore,
a.push_back(T());
may cause a to allocate new space, and under the hood there are copies made to the new underlying storage from the old.
Furthermore, there is nothing in the C++03 standard that says an implementation actually has to call allocator.construct, and in fact many (gcc for example) do not.
The C++0x standard adds new member functions to the container interface for moveable types, and clarifies how things like operator= behave in their presence.
See www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2486.pdf