What is the purpose of allocator_traits<T> in C++0x? - c++

Why isn't standard C++03 interface for querying member types for allocators used in C++0x? What are the use cases where member types are not sufficient?

To explain allocator_traits in terms of design pattern, its an Adapter to wrap your custom Allocator that meets far less implementation requirement (no need for construct, destroy, all those typedefs ...) and turns it into FlyWeight object that completes the rest of the Allocator implementation requirement for you with static members and types.
With allocator_traits, you just need to provide minimum of 10 lines of code for your custom allocator, as per page 3 of open-std doc Scoped Allocator Model (thx to #icecrime for mentioning).
I think of allocator_traits and allocator as a nice real-world example of turning non-FlyWeight object into FlyWeight in order to relieve the burden of implementaion details. Its a good API design practice for turning a class into FlyWeight that should've been FlyWeight in the first place.
To Java Programmers, in terms of design pattern, std::allocator_traits is like package private classes CharacterDataLatin1, ChracterData00, CharacterData0E, 01, 02 ... that inherits from java.lang.CharacterData to provide static Unicode definition and helpers that are to be shared by every instance of Character (std::allocator) classes.
Edit:
Another advantage of indirectly calling your custom allocator through allocator_traits is that it guarantees foward compatibility (page 3 of Scoped Allocator Model).
Number of requirements may grow in the future, and even if your ignorant on implementing new requirements to your allocator, those requirements will already be there in allocator_traits implemented by the compiler manufacturer. Knowing that C++ containers call allocator indirectly by allocator_traits, the STL containers using your custom allocator will be benefiting from the new requirements without the need for you to change your code.

I'm not familiar with this kind of things (at all), but this document seems like a good starting point to get a grasp on the rationale behind allocator_traits :
The keystone of this proposal is the
definition of an allocator_traits
template containing types and static
member functions for using allocators,
effectively replacing the Allocator concept that was lost in
Frankfurt.

Related

std::uninitialized_move with specific allocator

I'm writing a custom container template class that, as many (if not all) the containers in the stl, can use a specified allocator type.
To implement the range insert function, I need to move some of the elements in the container a number of spaces forward, where the memory is still uninitialized. To do this I want to use some (nonexistent) version of std::uninitialized_move() that uses the allocator in the container.
The other option is to do the move-construction of the objects using the allocator in a for loop and destroy the constructed objects in case of an exception. That's basically re-implementing std::uninitialized_move() with an extra feature.
The standard library implementation for my compiler (GCC) has exactly the functions I need (std::__uninitialized_move_a(), std::__uninitialized_copy_a(), etc), and are in fact used in the implementation of std containers, but I think those are rather compiler-specific.
Should I use this functions (portability)? Or is there other, more practical, option?
Maybe there is something in the standard library that I'm missing.
Should I use this functions (portability)?
You shouldn't use the GCC internal functions.
No, there doesn't seem to be standard equivalents. You can write your own versions of those functions. If you do, note that CustomAlloc::construct is an optional function (for example, std::allocator doesn't have this function since C++20), so it should be use through std::allocator_traits<CustomAlloc>::construct. This has additional benefit of being constexpr since C++20.
Or is there other, more practical, option?
One option is to ignore the possibility that CustomAlloc::construct has been implemented to do something other than direct placement new, and thereby simply use the standard std::uninitialized_move.
This technically limits the allocators that your container supports in theory, but on the other hand I've never seen such custom allocator used in practice. This may be reasonable limitation if the container is for internal use at least. If you do this, document the behaviour carefully.

In creating a bespoke container class, should I even provide a NON-polymorphic allocator option?

I am in the process of writing a bespoke container class, somewhat streamlined for a particular purpose at work, but potentially of much wider use. (That's why am doing this during weekends so it doesn't end up as company IP. ;-) )
Looking at pmr::polymorphic_allocator (and e.g. this question), I am wondering if I even should provide a way to use the container with a non-polymorphic allocator (i.e. an allocator provided as template parameter). It seems to me that, with the advent of pmr::polymorphic_allocator, the non-polymorphic versions of the standard containers are basically kept for compatibility (which, obviously, is not an issue for a new container class).
Am I missing some technical consequence of only providing the "new" (polymorphic) allocator scheme?

Why don't iterators depend on allocators? (i.e. does't making iterators SCARY violate the allocator's typedef abstractions?)

When reading about SCARY iterators in C++11, I see:
From a compiler perspective there is nothing wrong here. From a practical standpoint, however, there isn’t any semantic dependence between list iterators and list allocators. And, in general for all STL containers, iterators only depend (semantically speaking) on the container element type.
There's something I don't understand about that though:
Iterators return reference and pointer types when operator * or operator-> is applied to them.
reference, pointer, difference_type, etc. are derived from the typedefs inside the allocator.
But the allocator doesn't necessarily have to define pointer as value_type * (or even difference_type as ptrdiff_t), for example.
How can an iterator (a SCARY one) possibly know what data type(s) to return without knowing the allocator?
Don't iterators inherently depend on their allocators for these typedefs?
Allocators were initially designed to provide an interface into the memory model of the platform for which the program was compiling. Most current architectures (if not all) provide a flat memory model, and a single pointer type can be used to address memory in any program (no need for near and far pointers anymore).
That is reflected in C++11 in allocator_traits. Now it is not mandatory for an allocator to provide many of the typedefs that were previously required, including pointer_type or reference_type, difference_type and so on as there is a known good default value. This really maps to current practice, in most STL implementations those types are the same in all allocators.
Whenever the allocator does not provide the typedefs or the types are the same as the default value provided in allocator_traits there is no point in differentiating the iterators based on what allocator was used to construct the container. Where that assumption does not hold, the implementation could determine to use a different iterator type only for those allocators that provide a different set of typedefs than the default.
Note that I have not looked at their implementation, so take this at face value: that would support the intended purpose (minimize the generated code without breaking standard compliance).
Also note that this is not the only approach. The current allocator model in C++11 supports the use of polymorphic allocators, even if the standard does not yet provide an implementation. With polymorphic allocators a single allocator template argument (a polymorphic adaptor) provides the typedefs and the interface with the container, internally managing a pointer to a real allocator used to provide the memory.
The purpose of polymorphic allocators is the same as the paper referred by in the linked article: allow the creation of vocabulary types. Where the SCARY allocators focus on providing a vocabulary type for the iteration over a particular container type, polymorphic allocators go one step further and enable the use of the container itself as a vocabulary type, regardless of what the actual mechanism to acquire memory is.
You can find a reference implementation of polymorphic allocators (using different names) in Bloomberg's BSL

Why doesn't std::priority_queue have a clear() member function

I was doing some hacking today and found out that std::priority_queue does not have a clear() member function. Are there any technical reasons as to why the standards committee may have left this out?
To be clear, I am aware that it is easy to work around this via assignment:
oldPQ = std::priority_queue<int>{};
This solution is less desirable because:
It requires you to repeat the type - this does not continue to work under maintenance. As #chris pointed out below, you can simplify this if you're using the default constructor, but if you have a custom comparator, this may not be possible.
std::priority_queue cannot be used in a templated function that expects a clear() member function.
It is generally undesirable that it does not meet the common interface provided by the other containers. In particular, everything from std::forward_list to std::unordered_map to std::string has clear(). The only other exceptions I note are std::array, for which the semantics would not make sense, and std::stack and std::queue, for which the semantics are more questionable when std::deque works without any extra effort.
One item that looks like an issue, but in practice needn't be:
Because the internal container used for std::priority_queue is templated and may not have a clear() member function of its own, this creates an interesting problem, in particular it raises the question of backward compatibility. This is a non-issue because:
For internal containers that do not provide clear(), as long as nobody attempts to invoke std::priority_queue::clear(), the code will continue to compile.
It may still be possible with SFINAE to provide the new interface (the clear member) by calling clear() on the internal container when it's available and by repeatedly popping if it is not.
It is my opinion that this is a defect in the C++ standard. Assuming a technical discussion does not provide a strong case for why this method is omitted, I intend to pursue the creation of a standards proposal.
Edit:
Seems this is being handled in-committee (note the last post): https://groups.google.com/a/isocpp.org/forum/?fromgroups#!searchin/std-discussion/clear/std-discussion/_mYobAFBOrM/ty-2347w1T4J
http://wg21.cmeerw.net/lwg/issue2194
The specification of container adaptors is known to be overly pedantic: since the "abstract" spec of the corresponding data structure (from some book on abstract algorithms and data structures) does not include operation clear for canonical priority queues or stacks, it is not provided in the adaptor. This indeed often makes it quite inconvenient to use these adaptors in practice.
The good news though is that the inner container member is declared inside the adaptor as a protected member of the adapter, named c. This is probably done specifically for you to be able to easily implement your own version of the adaptor: by inheriting from the standard adaptor and adding whatever member functions you want to add, including clear.
As for comparing these adaptors' interfaces with standard container interfaces... I don't think it is a valid comparison. These adaptors have never been intended to be compatible with containers in terms of interface. Quite the opposite, the purpose of these adaptors was largely to restrict the public interface of the data structure and force it into the narrow bounds of what is allowed by its canonical abstract definition.
For example, you are not allowed to iterate over the canonical stack. Stack, by definition, is not "iterable". The fact that stack adaptor disables iteration interface is a good thing. But the absence of clear certainly feels too pedantic, since it has a great practical value without looking like a big violation of the canonical interface.

Per-object Data in Allocators?

What is "Per-object Data in Allocators". I can't seem to find what this means. Anyone have a good explanation or link for what this means in terms of the C++ Language?
CLARIFICATION
Section 19.4.2 "The C++ Programming Language (Special Edition)" pg. 573
"Similarly, if allocators were allowed to be perfectly general, the rebind mechanism that allows an allocator to allocate elements of arbitrary types would have to be more elaborate. Consequently, a standard allocator is assumed to hold no per-object data and an implementation of a standard container may take advantage of that."
Per-object data or local state refers to any non-static data members in the allocator class.
The issue is that currently (in c++03) there is no support for allocators with so-called local state. This is often considered to be a flaw with the allocator model in present-day c++.
Have a read through this article that details the design for a custom allocator. A paragraph under Design specifically addresses some of the pitfalls of allocators that incorporate local state.
Briefly, some operations in the standard library currently require that objects of a particular type be safely allocated by one instance of an allocator and deallocated by another instance of the allocator (both allocators are of the same type - of course!). This can be the case when implementing list::splice for instance. If allocators are allowed to have local state this can get tricky...
In the upcoming c++0x revision, it appears that allocators will be allowed to incorporate local state, check out the scoped allocator section here.
Hope this helps.
It simply means that std::allocator<T> does not contain any per-instance data members ... it is mainly a wrapper around memory allocation and deallocation functions, and also contains definitions of certain required typedefs, as well as mechanisms for rebinding an existing allocator so that it can allocate types that were not part of the original allocator template instantiation. So basically what is being stated is that if there were actual private data-members that had to be managed, especially in light of the requirement for a STL allocator to allow rebinding, that could greatly complicate the implementation of a generic allocator depending on what those per-instance data-members represented.