Stack Exchange
How to use Stl custom allocator that needs non-default constructor
I am trying to use stl with a custom allocator that needs to use non-default constructor. I follow the example in this link 
Custom STL Allocator with a custom constructor and get something like this:
void * start = new uint8_t[100];
Engine::BlockAllocator block_allocator = Engine::BlockAllocator(start, 100, 8, 8);
std::vector<uint64_t, Engine::StlAllocator<uint64_t>> a((Engine::StlAllocator<uint64_t>(&block_allocator)) );

My StlAllocator class
template <class T>
class StlAllocator {
public:
    using value_type = T;

    StlAllocator(IAllocator * allocator):_allocator(allocator) {}

    StlAllocator(StlAllocator && allocator): _allocator(allocator._allocator) {
        allocator._allocator = nullptr;
    }
    // need to have this specific signature to work with stl
    value_type* allocate(std::size_t n) {
        AllocateArray<value_type>(*_allocator, n);
    }
    // need to have this specific signature to work with stl
    void deallocate(value_type* p, std::size_t) noexcept {
        DeallocateArray<value_type>(*_allocator, p);
    }

    //TODO: Delete these constructors after I figured out what wrong with the custom allocator
    StlAllocator(const StlAllocator& allocator):_allocator(allocator._allocator) {}
    StlAllocator& operator=(const StlAllocator& allocator) {
        _allocator = allocator._allocator;
    }

private:
    // 8 bytes
    IAllocator * _allocator;
};

template <class T, class U>
bool operator==(StlAllocator<T> const&, StlAllocator<U> const&) noexcept {
    return true;
}

template <class T, class U>
bool operator!=(StlAllocator<T> const& x, StlAllocator<U> const& y) noexcept {
    return !(x == y);
}

IAllocator class
class IAllocator {
    template <class T> friend T* OperatorNew(IAllocator & allocator);
    template <class T> friend void OperatorDelete(IAllocator & allocator, T * object);
    template <class T> friend T* AllocateNew(IAllocator & allocator);
    template <class T> friend T* AllocateNew(IAllocator & allocator, T & t);
    template <class T> friend T* AllocateNew(IAllocator & allocator, T && t);
    template <class T> friend void DeallocateDelete(IAllocator & allocator, T * object);
    template <class T> friend T* AllocateArray(IAllocator & allocator, size_t length);
    template <class T> friend void DeallocateArray(IAllocator & allocator, T * array);
public:
    IAllocator(void * start, uint64_t size);
    IAllocator(IAllocator&& other);
    void * GetStart() const;
    uint64_t GetNumAlloc() const;
    uint64_t GetUsed() const;
    uint64_t GetTotalSize() const;
    uint64_t GetFree() const;
    void Reset();
    virtual ~IAllocator();
private:
    virtual void * Allocate(uint64_t size, uint8_t alignment) = 0;
    virtual void Deallocate(void * ptr) = 0;

protected:
    // virtual pointer table 8 bytes 
    // 8 bytes
    uint64_t _used;
    // 8 bytes
    uint64_t _total_size;
    // 8 bytes
    uint64_t _num_alloc;
    // 8 bytes
    void * _start;
};

template <class T> T* OperatorNew(IAllocator & allocator) {
    void * ptr = allocator.Allocate(sizeof(T), alignof(T));
    if (ptr == nullptr) {
        std::cout << "Allocator has run out of space\n";
        return nullptr;
    }
    return reinterpret_cast<T*>(ptr);
}

template <class T> void OperatorDelete(IAllocator & allocator, T * object) {
    assert(object != nullptr);
    allocator.Deallocate(object);
}



template <class T> T* AllocateNew(IAllocator & allocator) {
    void * ptr = allocator.Allocate(sizeof(T), alignof(T));
    if (ptr == nullptr) {
        std::cout << "Allocator has run out of space\n";
        return nullptr;
    }
    /*std::thread::id this_id = std::this_thread::get_id();
    std::cout << "Allocate in thread " << this_id << "\n";*/
    return new (ptr) T;
}

template <class T> T* AllocateNew(IAllocator & allocator, T & t) {
    void * ptr = allocator.Allocate(sizeof(T), alignof(T));
    if (ptr == nullptr) {
        std::cout << "Allocator has run out of space\n";
        return nullptr;
    }
    /*std::thread::id this_id = std::this_thread::get_id();
    std::cout << "Allocate in thread " << this_id << "\n";*/
    return new (ptr) T(t);
}

template <class T> T* AllocateNew(IAllocator & allocator, T && t) {
    void * ptr = allocator.Allocate(sizeof(T), alignof(T));
    if (ptr == nullptr) {
        std::cout << "Allocator has run out of space\n";
        return nullptr;
    }
    /*std::thread::id this_id = std::this_thread::get_id();
    std::cout << "Allocate in thread " << this_id << "\n";*/
    return new (ptr) T(std::move(t));
}

template <class T> void DeallocateDelete(IAllocator & allocator, T * object) {
    assert(object != nullptr);
    object->~T();
    /*std::thread::id this_id = std::this_thread::get_id();
    std::cout << "Deallocate in thread " << this_id << "\n";*/
    allocator.Deallocate(object); 
}

template <class T> T* AllocateArray(IAllocator & allocator, size_t length) {
    size_t header_size = sizeof(size_t) / sizeof(T); // find header_size as a multiple of T, an array only needs one header
    if (sizeof(size_t) % sizeof(T) > 0) {
        header_size += 1;
    }
    void * alloc_ptr = allocator.Allocate(sizeof(T)*(header_size + length), alignof(T)); // allocate enough size for an array 
    if (alloc_ptr == nullptr) {
        std::cout << "Allocator has run out of space\n";
        return nullptr;
    }
    T* p = reinterpret_cast<T*>(alloc_ptr) + header_size; // the returned ptr is at an offset of headerSize from the address returned by allocator
    *(reinterpret_cast<size_t*>(p) - 1) = length; // set the header to the length of the array
    for (size_t i = 0; i < length; i++) {
        new (p + i) T; // construct objects in the memory 
    }
    /*std::thread::id this_id = std::this_thread::get_id();
    std::cout << "Allocate in thread " << this_id << "\n";*/
    return p;
}

template <class T> void DeallocateArray(IAllocator & allocator, T * array) {
    assert(array != nullptr);
    size_t * header_ptr = (reinterpret_cast<size_t*>(array) - 1); // find address of header
    size_t length = *header_ptr;
    for (size_t i = 0; i < length; i++) {
        (array + i)->~T(); // call destructor of objects
    }
    size_t header_size = sizeof(size_t) / sizeof(T); // find header_size as a multiple of T
    if (sizeof(size_t) % sizeof(T) > 0) {
        header_size += 1;
    }
    /*std::thread::id this_id = std::this_thread::get_id();
    std::cout << "Deallocate in thread " << this_id << "\n";*/
    allocator.Deallocate(array - header_size); // deallocate using the address returned by allocation, which is computed by array - header_size
}

BlockAllocator.h
class BlockAllocator: public IAllocator {
public:
    BlockAllocator(void * start, uint64_t size, uint64_t block_size, uint8_t block_alignment);
    BlockAllocator(BlockAllocator&& other);
    ~BlockAllocator() override;
    BlockAllocator(const BlockAllocator&) = delete;
    BlockAllocator& operator=(const BlockAllocator&) = delete;
private: 
    void * Allocate(uint64_t size, uint8_t alignment) override;
    void Deallocate(void * ptr) override;
private:
    // 8 bytes virtual table function pointer
    // 32 bytes IAllocator
    // 8 bytes
    uint64_t _block_size;
    // 8 bytes
    void ** _free_list;
    // 1 bytes
    uint8_t _block_alignment;
    // 7 bytes
    uint8_t _padding[7];

};

BlockAllocator.cpp (this is a stripped down minimal version)
BlockAllocator::BlockAllocator(void * start, uint64_t size, uint64_t block_size, uint8_t block_alignment): IAllocator(start, size), 
    _block_size(block_size), 
    _block_alignment(block_alignment) {
}

BlockAllocator::BlockAllocator(BlockAllocator&& other):IAllocator(std::move(other)), 
    _block_size(other._block_size), 
    _free_list(other._free_list), 
    _block_alignment(other._block_alignment) {}

BlockAllocator::~BlockAllocator() {
    _free_list = nullptr;
}

void * BlockAllocator::Allocate(uint64_t size, uint8_t alignment) {
    void * ptr = operator new(size);
    return ptr; 
}

void BlockAllocator::Deallocate(void * ptr) {
    delete ptr;
}

I am using c++17 on visual studio. The error I got is this
Error   C2664   'Engine::StlAllocator<_Newfirst>::StlAllocator(const Engine::StlAllocator<_Newfirst> &)': cannot convert argument 1 from 'Engine::StlAllocator<uint64_t>' to 'Engine::IAllocator *'


What is ptr ?

Please provide [mcve].

All constructuctors of STL containers take as a last argument const Allocator& alloc = Allocator(), see here for std::vector. That can be used if the allocator has no default constructor.

@sv90 so you are suggesting I make my allocator variable const?

@HenryWise no. It is only passed by const reference and copy-constructed into the container

@sv90 but that is exactly what I am doing isnt it?

Are you getting this error with or without StlAllocator(const StlAllocator& allocator) constructor? The TODO comment appears to suggest that the error may be going away once it's added. An allocator is indeed required to be copyable, so you need that constructor.

@IgorTandetnik I get the same error even if I have copy constructor for StlAllocator

Ah, I see what's going on. Not only is Allocator<T> required to be constructible from Allocator<T> - it's also required to be constructible from Allocator<U> for any type U. The container is allowed to "rebind" the allocator to obtain an allocator for types other than T (e.g. std::list<T> would generally need to allocate some internal type Node<T>, not T itself). In your case, the container rebound StlAllocator<uint64_t> to obtain an instance of the allocator capable of allocating some internal type _Newfirst, then tried to construct the latter from the former.

StlAllocator<T> doesn't meet allocator requirements. In particular, it is required to be constructible from StlAllocator<U> for any type U, not just from StlAllocator<T>.
The error suggests that the container using your StlAllocator<uint64_t> actually needs an allocator for some internal type (named _Newfirst), and so tries and fails to construct the latter from the former.

How do you suggest I fix this? Should i do something similar to this? "
template  struct rebind { typedef stl_allocator other; };
template  stl_allocator(const stl_allocator&){}" . Cause I see many examples online that dont need this.

You don't need to provide an explicit rebind; modern compilers can rebind implicitly. You do indeed need to provide a templated constructor from StlAllocator<U>. The example you cite does provide such a constructor.
