Custom allocator method is not called

Viewed 265

I am trying to learn and write a self custom allocator - I was expecting that the cout statement should be printed but it never does - what is the wrong am I doing - how to write custom allocator:

#include <iostream>
#include <vector>

template < class T >
class MyAllocator : public std::allocator<T> {
public:
    T* allocate(size_t size)
    {
        std::cout << "Allocation request size " << size << std::endl;
        return new T[size];
    }

};

int main()
{
    std::vector <int, MyAllocator<int>> x;
    x.push_back(10);
    x.push_back(10);
    x.push_back(10);

    for (auto& var : x)
        std::cout << "Value " << var << std::endl;

}

Output

Value 10
Value 10
Value 10
2 Answers

The inheritance of the standard allocator is not needed. Remove the inheritance : public std::allocator<T> and the compiler will be kind to inform you about what you missed to implement. Until C++17 the method construct must be implemented and it is used in std::vector, not allocate. Also value_type, deallocator and destroy are missing.

template< class U, class... Args >
void construct( U* p, Args&&... args );

Since you haven't implemented it and your allocator inherits the standard allocator, std::allocator::construct is called, that does not produce output.

Why you should not inherit std::allocator

The answer on this question is simple on the one hand and not simple in practice on the other hand.

  1. Like other classes in C++ standard library, std::allocator does not have a virtual destructor, so it should not be inherited, if it is not explicitly indicated like in std::enable_shared_from_this.
  2. Standard containers don't use class Allocator directly. They use std::allocator_traits. It helps to implement minimal user-defined allocators. If you implement only value_type, allocate and deallocate members of MyAllocator, std::allocator_traits<MyAllocator> makes MyAllocator fully conformed to C++ named requirements: Allocator.

    • Let look at your MyAllocator carefully. It inherits std::allocator and "replaces" std::allocator::allocate (2).
    • Let read std::allocator_traits::allocate (2) reference manual, that is called by std::vector:

      Calls a.allocate(n, hint) if possible. If not possible (e.g. a has no two-argument member function allocate()), calls a.allocate(n).

    What you have reached. You have implemented MyAllocator::allocate(std::size_t n) (2), but not MyAllocator::allocate(std::size_t n, const void* hint) (1), it is inherited from std::allocator. It is called from std::vector, that is not what you expected. If you had not inherited std::allocator, your implementation MyAllocator::allocate would be called.

You're half way there. As described here, the standard describes custom allocator classes having several optional and required named attributes and methods. If you want your example to work, at least you must implement the required ones. These are:

  • add a value_type.
  • add an allocate(n) method. Takes a size_t input and returns an address.
  • add a deallocate(p, n) method. Takes two inputs and has no output.

You must also remove the inheritance to std::allocator (see @S.M.'s answer). Example similar to your MyAllocator class:

#include <iostream>
#include <vector>
#include <cstdlib>

template < class T >
class MyAllocator
{
public:
    T * allocate(size_t size)
    {
        std::cout << "Allocation request size => " << size << std::endl;
        return new T[size];
    }
    void deallocate(T * p_t, size_t n)
    {
        std::free(p_t);
    }
    using value_type = T;
};

int main()
{
    std::vector <int, MyAllocator<int>> x;
    x.push_back(10);
    x.push_back(10);
    x.push_back(10);

    for (auto& var : x)
        std::cout << "Value " << var << std::endl;

}
Related