Home > Enterprise >  How do I correctly pass converting constructor from an std::queue through to the underlying std::deq
How do I correctly pass converting constructor from an std::queue through to the underlying std::deq

Time:04-05

I have created a custom memory allocator. To use it with STL Containers, I've also created a wrapper so that it adheres to the std::allocator_traits requirements.

#include <cstdint>
#include <memory>
#include <deque>
#include <queue>

class CustomAlloc
{
public:
    CustomAlloc(const std::size_t& maxMem) noexcept
    :
        m_maxMem(maxMem),
        m_usedMem(0)
    {

    }

    CustomAlloc(CustomAlloc&& other) noexcept
    :
        m_maxMem(other.m_maxMem),
        m_usedMem(other.m_usedMem)
    {

    }

    CustomAlloc& operator=(CustomAlloc&& other) noexcept
    {
        m_maxMem = other.m_maxMem;
        m_usedMem = other.m_usedMem;
        return *this;
    }

    CustomAlloc(const CustomAlloc&) = delete;
    CustomAlloc& operator=(CustomAlloc&) = delete;

    [[nodiscard]] void* Allocate(const std::size_t& size)
    {
        if(m_usedMem   size > m_maxMem)
        {
            throw std::bad_alloc();
        }

        void* ptr = std::malloc(size);

        if(ptr == nullptr)
        {
            throw std::bad_alloc();
        }

        m_usedMem  = size;

        return ptr;
    }

    void Free(void* const ptr) const
    {
        return std::free(ptr);
    }

    const std::size_t& GetMaxMem() const noexcept
    {
        return m_maxMem;
    }

private:
    std::size_t m_maxMem;
    std::size_t m_usedMem;
};

template<typename T, typename Alloc>
class STLAdaptor
{
public:

    typedef T value_type;

    STLAdaptor(Alloc& allocator) noexcept
    :
        m_allocator(allocator)
    {

    }

    template<typename U>
    STLAdaptor(const STLAdaptor<U, Alloc>& other) noexcept
    :
        m_allocator(other.m_allocator)
    {}

    [[nodiscard]] constexpr T* allocate(std::size_t n)
    {
        return reinterpret_cast<T*>
            (m_allocator.Allocate(n * sizeof(T)));
    }

    constexpr void deallocate(T* p, [[maybe_unused]] std::size_t n)
    {
        m_allocator.Free(p);
    }

    std::size_t MaxAllocationSize() const
    {
        return m_allocator.GetMaxMem();
    }

    bool operator==(const STLAdaptor<T,Alloc>& rhs)
    {
        return m_allocator.GetStart() == rhs.m_allocator.GetStart();
    }

    bool operator!=(const STLAdaptor<T,Alloc>& rhs)
    {
        return !(*this == rhs);
    }

    Alloc& m_allocator;
};

template<typename T, typename Alloc>
using CustomDeque = std::deque<T, STLAdaptor<T,Alloc>>;

template<typename T, typename Alloc>
using CustomQueue = std::queue<T, CustomDeque<T, Alloc>>;

The issue I'm facing:

int main()
{
    CustomAlloc customAlloc(3000000);

    CustomDeque<int, CustomAlloc> customDeque(customAlloc);

    CustomQueue<int, CustomAlloc> customQueue(STLAdaptor<int, CustomAlloc>{customAlloc});

    return 0;
}

This works perfectly fine, but it's code-gore to have to pass in the STLAdaptor<int, CustomAlloc>{customAlloc} where all other STL Containers can rely on the converting constructor for the STLAdaptor.

What I'd like to do is:

int main()
{
    CustomAlloc customAlloc(3000000);

    CustomDeque<int, CustomAlloc> customDeque(customAlloc);

    CustomQueue<int, CustomAlloc> customQueue(customAlloc);

    return 0;
}

Where the customAlloc is somehow passed to the underlying std::deque.

CodePudding user response:

If you can't change CustomAlloc code as Ted Lyngmo suggested, the alternative is to define CustomQueue as a subclass of std::queue:

template<typename T, typename Alloc>
struct CustomQueue : std::queue<T, CustomDeque<T, Alloc>>
{
    CustomQueue(Alloc& alloc) :std::queue<T, CustomDeque<T, Alloc>>(STLAdaptor<T, Alloc>(alloc)) {}
};

Now you can construct it by passing merely the allocator:

int main()
{
    CustomAlloc customAlloc(3000000);

    CustomDeque<int, CustomAlloc> customDeque(customAlloc);

    CustomQueue<int, CustomAlloc> customQueue(customAlloc);

    customQueue.push(5);

    return 0;
}

This code was tested on VS 2019.

  • Related