Search code examples
c++allocator

No matching constructor for initialization of my custom allocator


I wrote a custom allocator. However, the Clang/LLVM compiler started complaining about mismatching a constructor for initialization of my allocator when I added list.sort() into main().

Though the code is a bit long, this is the minimum workable snippet:

#include <iostream>
#include <ctime>
#include <list>
#include <limits>

template<typename T, int start = 16, int ratio = 2, int thrsh = 65536>
class Allocator
{
private:
    T  *avsp;
    int used, vcnt;
    struct _block
    {   struct _block *next;
        T nodes[1];
    } *pool, *pblock;

public :
    //    typedefs
    typedef T value_type;
    typedef value_type* pointer;
    typedef const value_type* const_pointer;
    typedef value_type& reference;
    typedef const value_type& const_reference;
    typedef std::size_t size_type;
    typedef std::ptrdiff_t difference_type;

public :
    // convert an allocator<T> to allocator<U>
    template<typename U>
    struct rebind
    {
        typedef Allocator<U, start, ratio, thrsh> other;
    };

public :
    explicit Allocator()
    {
        avsp = NULL;
        used = 0;
        vcnt = 0;
        pool = NULL;
        pblock = NULL;
    }

    ~Allocator() {}
    explicit Allocator(Allocator const&) {}

    template<typename U>
    explicit Allocator(Allocator<U, start, ratio, thrsh> const&) {}

    // address
    pointer address(reference r)
    {
        return &r;
    }
    const_pointer address(const_reference r)
    {
        return &r;
    }

    // memory allocation
    pointer allocate(size_type cnt = 1,  // SHOULD ALWAYS BE ONE
                     typename std::allocator<void>::const_pointer = 0)
    {
        (void)cnt;

        if (avsp == NULL)
        {
            if (vcnt == 0)
            {
                pblock = pool;
                pool   = NULL;

                if (used == 0)
                    vcnt = (used = start);
                else
                    vcnt = (used < thrsh) ? (used *= ratio) : (used = thrsh);

                if (pool != NULL)
                    std::cerr << "Potential Memory Leak." << std::endl;  // Compatibility Purpose Only

                pool = static_cast<struct _block*>(malloc((sizeof(*pblock)) +
                (sizeof(pblock->nodes)) * (size_t)(used - 1)));

                if (pool == NULL)
                    std::cerr << "Memory Allocation Failure." << std::endl;  // Compatibility Purpose Only

                pool->next = pblock;
            }

            return &(pool->nodes[--vcnt]);
        }
        else
        {
            // NOT IMPL: AVSP
            exit(EXIT_FAILURE);
        }

        // NEVER REACH !!
        exit(EXIT_FAILURE);
    }
    void deallocate(pointer p, size_type)
    {
        // NOT IMPL: AVSP
        (void)p;
    }

    // size
    size_type max_size() const
    {
        return std::numeric_limits<size_type>::max() / sizeof(T);
    }

    // construction/destruction
    void construct(pointer p, const T& t)
    {
        new(p) T(t);
    }
    void destroy(pointer p)
    {
        p->~T();
    }

    template<typename U>
    bool operator==(const Allocator<U, start, ratio, thrsh>&) const
    {
        return true;
    }

    template<typename U>
    bool operator!=(const Allocator<U, start, ratio, thrsh>&) const
    {
        return false;
    }
};

int main (void)
{
    std::list<uint32_t, Allocator<uint32_t>> list;

    for (int cnt = 0; cnt < 1 << 27; cnt++)
        list.push_back(rand());

    list.sort();  // <-- Problems Here

    return 0;
}

This is the error message:

/usr/include/c++/4.6/bits/move.h:127
error: no matching constructor for initialization of 'Allocator<std::_List_node<unsigned int>, 16, 2, 65536>'
/usr/include/c++/4.6/bits/allocator.h:163:4:
in instantiation of function template specialization 'std::swap<Allocator<std::_List_node<unsigned int>, 16, 2, 65536> >' requested here
/usr/include/c++/4.6/bits/stl_list.h:1185:4:
in instantiation of member function 'std::__alloc_swap<Allocator<std::_List_node<unsigned int>, 16, 2, 65536>, false>::_S_do_it' requested here
/usr/include/c++/4.6/bits/list.tcc:375:11:
in instantiation of member function 'std::list<unsigned int, Allocator<unsigned int, 16, 2, 65536> >::swap' requested here
<this-file>.cpp:??:10:
in instantiation of member function 'std::list<unsigned int, Allocator<unsigned int, 16, 2, 65536> >::sort' requested here

Solution

  • Types with explicit copy constructors do not meet the CopyConstructible requirements, and allocators must be CopyConstructible. This was clarified by https://wg21.link/lwg2081

    So you need to remove the explicit from your copy constructor. To be safe (and ensure portability to all standard library implementations) you should probably also remove explicit from the converting constructor template.