Unlocking the Power of Custom Allocators

A Common Pitfall

Imagine you’ve crafted a custom allocator, ShortAlloc, to optimize memory management in your application. You’re excited to integrate it into your code, but when you try to use it with a std::vector, you’re met with a frustrating error. The issue arises because the custom allocator becomes part of the type, making std::vector<int> and std::vector<int, ShortAlloc> incompatible.

Understanding the Problem

The root cause of this issue lies in the way allocators are used with template classes from the standard library. When you pass a custom allocator as a template parameter, it becomes an integral part of the type. This means that std::vector<int> and std::vector<int, MyAlloc> are distinct types, making them incompatible.

The Default Allocator

So, what allocator does std::vector<int> use by default? The answer lies in the default template argument, std::allocator. This empty class relies on global new and delete operators to manage memory. Interestingly, the size of a container using an empty allocator is smaller than one using a custom allocator.

Memory Management Evolution

C++17 introduced a game-changing solution to this problem: the std::pmr namespace. This namespace provides a unified allocator, std::pmr::polymorphic_allocator, which dispatches allocation and deallocation requests to a memory resource class. By using this polymorphic allocator, you can create custom memory resources and avoid the hassle of writing new allocators.

Polymorphic Allocators in Action

The std::pmr::polymorphic_allocator acts as an extra layer of indirection, containing a pointer to the memory resource. This design allows for seamless integration with custom memory resources, such as your Arena class. The following diagram illustrates the control flow:

Figure 7.10: Allocating memory using a polymorphic_allocator

To leverage the power of polymorphic allocators, simply switch to the std::pmr namespace:
cpp
auto v1 = std::vector<int>{}; // Uses std::allocator
auto v2 = std::pmr::vector<int>{/*...*/}; // Uses polymorphic_allocator

Diving Deeper

If you’re curious about the implementation of std::vector in libc++, you’ll find that it employs a clever type called compressed_pair. This optimization technique, based on the empty base-class optimization, eliminates unnecessary storage occupied by empty classes. For more information, explore the Boost documentation on compressed_pair.

Leave a Reply

Your email address will not be published. Required fields are marked *