|
BDE 4.14.0 Production release
|
Macros | |
| #define | BSLMA_BSLALLOCATOR_DEPRECATE_ASSIGN |
Provide an STL-compatible proxy for bslma::Allocator objects.
bsl::allocatorCanonical header: bsl_memory.h
This component provides an STL-compatible proxy for any allocator class derived from bslma::Allocator. The proxy class, bsl::allocator is a template that adheres to the allocator requirements defined in section 20.5.3.5 [allocator.requirements] of the C++17 standard. bsl::allocator may be used to instantiate any class template that is parameterized by a standard allocator. The container is expected to allocate memory for its own use through the allocator. The bsl::allocator object holds a pointer to an object (the allocation mechanism) of class type derived from bslma::Allocator. Different mechanism types allocate memory in different ways or from different pools, so this approach gives the programmer run time control over how the container obtains memory.
The bsl::allocator template is intended to solve a problem created by the C++ standard allocator protocol. In STL, the allocator type is specified at compile time as a container template parameter, so the allocation mechanism becomes an explicit part of the resulting container type. Two containers cannot have the same type unless they are instantiated with the same allocator type. The bsl::allocator template breaks the connection between the compile-time allocator type and the run-time allocation mechanism. The allocation mechanism is chosen at run-time by initializing (contrast with instantiating) the bsl::allocator with a pointer to a mechanism object derived from bslma::Allocator. Each class derived from bslma::Allocator implements a specific allocation mechanism and is thus called a mechanism class within this component. The bsl::allocator object forwards calls made through the standard allocator interface to the mechanism object with which it was initialized. In this way, two containers instantiated with bsl::allocator can use different allocation mechanisms even though they have the same compile-time type. The default mechanism object, if none is supplied to the bsl::allocator constructor, is bslma::Default::defaultAllocator().
A container constructs its elements by calling the construct method on its allocator. Importantly, bsl::allocator is a scoped allocator – when its construct method is called, the allocator passes itself to the constructor of the object being constructed (if that object is allocator aware (AA) and uses a compatible allocator type). Thus, a container instantiated with a scoped allocator ensures that its elements use the same allocator as the container itself. The bsl::allocator::construct method will propagate the allocator not only to element types that use bsl::allocator, but also to any types that use bslma::Allocator * – i.e., all type for which the bslma::UsesBslmaAllocator trait is true.
A container using bsl::allocator should not copy its allocator on assignment and thus assignment of bsl::allocator objects is almost always incorrect. Its base class, bsl::polymorphic_allocator, is not assignable, in fact but, for compatibility with some existing code, assignment of bsl::allocator must compile, but it is a precondition violation if the allocators being assigned are not already equal at run time (i.e., when assignment is a a no-op). The assignment operator is deprecated and might be removed in the future, once all existing uses have been excised.
Instantiations of bsl::allocator have reference semantics. A bsl::allocator object does not "own" the bslma::Allocator with which it is initialized; copying a bsl::allocator object does not copy its mechanism object and destroying a bsl::allocator does not destroy its mechanism object. Two bsl::allocator objects compare equal if and only if the mechanism objects they refer to compare equal.
The bsl::allocator class template was the inspiration for the C++17 std::pmr::polymorphic_allocator class template (section 23.12.3, [mem.poly.allocator.class] in the C++17 Standard) and bslma::Allocator was the inspiration for the C++17 std::pmr::memory_resource (section 23.12.2, mem.res.class] in the C++17 Standard). For compatibility with the C++17 standard, bsl::allocator is derived from bsl::polymorphic_allocator which, when using a C++17 library, is identical to std::pmr::polymorphic_allocator. Similarly, bslma::Allocator is derived from bsl::memory_resource, which is identical to std::pmr::memory_resource. These inheritance relationships ensure that a bsl::allocator instance can be passed to any type that is instantiated with a std::pmr::polymorphic_allocator, including pmr containers from the platform library. Similarly, a pointer to bslma::Allocator is implicitly convertible to both std::pmr::memory_resource * and std::pmr::polymorphic_allocator.
The allocator requirements section of the C++03 standard (section 20.1.5 [lib.allocator.requirements]) permits containers to assume that two allocators of the same type always compare equal, effectively limiting C++03 to stateless allocators. This assumption is incorrect for instantiations of bsl::allocator. Therefore, for a container (or other facility) to use bsl::allocator, it must operate correctly in the presence of non-equal bsl::allocator objects. In practice, this means that a container cannot transfer ownership of allocated memory to another container unless the two containers use equal allocators. Older third-party templates that assume stateless allocators might not work correctly when instantiated with bsl::allocator.
Because it is immutable, non-assignable, and has reference semantics, a single bsl::allocator object is safe for concurrent access by multiple threads if and only if the bslma::Allocator it references is safe for concurrent access from multiple threads. Separate objects of bsl::allocator type may safely be used in separate threads if and only if the bslma::Allocator objects they reference are, themselves, safe for concurrent access.
This section illustrates intended use of this component.
We first show how to define a container type parameterized with an STL-style allocator template parameter. To avoid issues concerning reallocation, dynamic growth, etc., we choose an array whose size is fixed at construction. Our array will accept any STL-compatible allocator; we do not assume as scoped allocator, which would dictate that we pass the allocator through to the parameterized T contained type (see the bslma_allocator component and bslma_constructionutil package).
We begin by defining member variables to hold the allocator, length, and allocated array:
Then, we define the public interface:
Next, we define the first constructor, which uses the allocator's allocate memory to obtain memory, then uses its construct method to construct each element. To provide a uniform and future-proof interface, the standard way to call allocate and construct is indrectly though bsl::allocator_traits. If ALLOC is a bsl::allocator object, then the construct method will attempt to pass the allocator to the constructed elements. Note that exception safety has been sacrificed for simplicity of presentation; a production version of my_FixedSizeArray would need to unwind any constructed elements and the allocation if an exception were thrown.
Next, we define the copy constructor, which initializes the allocator member but defers the rest of the work to the assignment operator:
Now we define the assignment operator, which allocates the array and copies elements from the rhs array. Note, again, that we simplified the code by omitting exception-safety constructs.
Next, we define the destructor, which uses the allocator's destroy method to destroy each element, then the allocator's deallocate method to return memory to the allocator:
The equality and inequality operators simply compare the lengths and element values of the two arrays:
Now we can create array objects with different allocator mechanisms. First we create an array, a1, using the default allocator and fill it with the values 1 .. 5:
Finally, we create a copy of a1 using a test allocator. The values of a1 and a2 are equal, even though they have different allocation mechanisms. We verify that the test allocator was used to allocate the new array elements:
In this example, we use the FixedSizeArray template defined in Example 1 and demonstrate how bsl::allocator propagates itself to the elements it constructs, such that the container and its elements all use the same allocator.
First, we create a representative element class, MyType, that allocates memory using the bslma::Allocator protocol:
Now, we instantiate my_FixedSizeArray using MyType and verify that, when we provide the address of an allocator to the constructor of the container, the same address is passed to the constructor of the container's elements:
Next, we copy-construct the container and verify that the copy uses the default allocator, not the allocator from the original; moreover, we verify that the elements stored in the copy also use the default allocator.
Finally, we create a third array using the test allocator and use assignment to give it the same value as the second array. We then verify that the assignment did not modify the allocator of the lhs array and that the elements of the resulting copy use the same allocator as the lhs array:
| #define BSLMA_BSLALLOCATOR_DEPRECATE_ASSIGN |