The concept of a polymorphic allocator from C++17 is an enhancement to standard allocators from the Standard Library.
It’s much easier to use than a regular allocator and allows containers to have the same type while having a different allocator, or even a possibility to change allocators at runtime.
Let’s see how we can use it and hack to see the growth of std::vector
containers.
In short, a polymorphic allocator conforms to the rules of an allocator from the Standard Library. Still, at its core, it uses a memory resource object to perform memory management.
Polymorphic Allocator contains a pointer to a memory resource class, and that’s why it can use a virtual method dispatch. You can change the memory resource at runtime while keeping the type of the allocator. This is the opposite to regular allocators which make two containers using a different allocator also a different type.
All the types for polymorphic allocators live in a separate namespace std::pmr
(PMR stands for Polymorphic Memory Resource), in the
header.
The Series
This article is part of my series about C++17 Library Utilities. Here’s the list of the articles:
- Refactoring with
std::optional
- Using
std::optional
- Error handling and
std::optional
- Everything You Need to Know About
std::variant
from C++17 - Everything You Need to Know About
std::any
from C++17 std::string_view
Performance and followup- C++17 string searchers and followup
- Conversion utilities – about from_chars.
- How to get File Size in C++? and std:filesystem::file_size Advantages and Differences
- How To Iterate Through Directories
Resources about C++17 STL:
- C++17 In Detail by Bartek!
- C++17 – The Complete Guide by Nicolai Josuttis
- C++ Fundamentals Including C++ 17 by Kate Gregory
- Practical C++14 and C++17 Features – by Giovanni Dicanio
- C++17 STL Cookbook by Jacek Galowicz
OK, let’s go back to our main topic: PMR.
Core elements of pmr
:
Here’s a little summary of the main parts of pmr
:
std::pmr::memory_resource
– is an abstract base class for all other implementations. It defines the following pure virtual methods:virtual void* do_allocate(std::size_t bytes, std::size_t alignment)
,virtual void do_deallocate(void* p, std::size_t bytes, std::size_t alignment)
virtual bool do_is_equal(const std::pmr::memory_resource& other) const noexcept
.
std::pmr::polymorphic_allocator
– is an implementation of a standard allocator that usesmemory_resource
object to perform memory allocations and deallocations.- global memory resources accessed by
new_delete_resource()
andnull_memory_resource()
- a set of predefined memory pool resource classes:
synchronized_pool_resource
unsynchronized_pool_resource
monotonic_buffer_resource
- template specialisations of the standard containers with polymorphic allocator, for example
std::pmr::vector
,std::pmr::string
,std::pmr::map
and others. Each specialisation is defined in the same header file as the corresponding container. - It’s also worth mentioning that pool resources (including
monotonic_buffer_resource
) can be chained. If there’s no available memory in a pool, the allocator will allocate from the “upstream” resource.
And we have the following predefined memory resources:
new_delete_resource()
It’s a free function that returns a pointer to a global “default” memory resource. It manages memory with the global new
and delete
.
null_memory_resource()
It’s a free function that returns a pointer to a global “null” memory resource which throws std::bad_alloc
on every allocation. While it sounds not useful, it might be handy when you want to guarantee that your objects don’t allocate any memory on the heap. Or for testing.
synchronized_pool_resource
This is a thread-safe allocator that manages pools of different sizes. Each pool is a set of chunks that are divided into blocks of uniform size.
unsynchronized_pool_resource
A non-thread-safe pool_resource
.
monotonic_buffer_resource
This is a non-thread-safe, fast, special-purpose resource that gets memory from a preallocated buffer, but doesn’t release it with deallocation. It can only grow.
An Example
Below you can find a simple example of monotonic_buffer_resource
and pmr::vector
:
#include
#include
#include
int main() {
char buffer[64] = {};
std::fill_n(std::begin(buffer), std::size(buffer) - 1, '_');
std::cout 'n