如何使用自定义分配器和自定义列表重新绑定

时间:2018-01-20 09:49:42

标签: c++

我实现了自定义分配器和自定义列表容器,它支持此分配器。它们的定义如下:

template <typename T, size_t num_of_blocks = 16>
class MyAllocator {
public:
    template <typename U>
    struct rebind {
        using other = MyAllocator<U, num_of_blocks>;
    }
    ...dozens of standard methods
}

template <typename T, typename MyAlloc = std::allocator<Node<T>>>
class MyList {
private:
    MyAlloc allocator;
    ...different methods which use allocator member    
}

这一切都很好。所以,在我的客户端代码中,我可以这样做:

auto my_list = MyList<int, MyAllocator<Node<int>,10>>{};

它没有错误,我看到,我的自定义分配器被使用。但我不喜欢我使用自定义分配器的方式。实际上,我希望我的客户端代码看起来像:

auto my_list = MyList<int, MyAllocator<int,10>>{};

我的第一次尝试是:

template <typename T, typename MyAlloc = std::allocator<T>>
class MyList {
private:
    //MyAlloc allocator; // remove this member and rebind allocator to another one
    typedef typename MyAlloc::template rebind<Node<T>>::other node_alloc_type;
    node_alloc_type allocator; // I expect that my allocator now is of type MyAllocator<Node<T>, num_of_blocks>
    ... all the rest left unchanged          
}

然而,当我运行新的客户端代码时:

auto my_list = MyList<int, MyAllocator<int,10>>{};

我收到以下错误消息:

  

无法在赋值

中将'int *'转换为'Node *'

我不确定我做错了什么,我错过了什么。那么,我该如何解决呢?在自定义容器中使用重新绑定的正确方法是什么?

修改

这就是我的自定义容器现在的样子:

//MyList.h

#include <memory>

template<typename T> 
struct Node
{
    Node(): m_next(nullptr){}

    Node(T const &t): 
        m_value(t), 
        m_next(nullptr)
    {}

    T m_value;
    Node* m_next;
};

template<typename T, typename MyAllocator = std::allocator<Node<T>>>
class MyList
{
private:
    Node<T>* m_head = nullptr;
    Node<T>* m_tail = nullptr;
    MyAllocator my_allocator;
public:

    class Iterator 
    {
    private:
        Node<T>* m_Node; 
    public:

        Iterator(Node<T>* Node): m_Node(Node) {};

        bool operator==(const Iterator& other)
        {
            return this == &other || m_Node == other.m_Node;        
        }

        bool operator!=(const Iterator& other)
        {
            return !operator==(other);        
        }

        T operator*()
        {       
            if (m_Node)
            {
                return m_Node->m_value;
            }
            return T();
        } 

        Iterator operator++() 
        {
            Iterator i = *this;
            if (m_Node) 
            {
                m_Node = m_Node->m_next;
            }
            return i;
        }       

    };


    template<typename... Args>
    void emplace(T v)    
    {
          auto new_Node = my_allocator.allocate(1);       
          my_allocator.construct(new_Node, v);        
          if (m_head)
          {
            m_tail->m_next = new_Node;        
          } else {
            m_head = new_Node;
            new_Node->m_next = nullptr;       
          }
        m_tail = new_Node;        
    }

    Iterator begin() const
    {
        return Iterator(m_head);
    }


    Iterator end() const
    {
        return Iterator(nullptr);
    }        

};

此时没有重新绑定,我必须将其定义为

template<typename T, typename MyAllocator = std::allocator<Node<T>>>
class MyList

我想要的是这样定义:

template<typename T, typename MyAllocator = std::allocator<T>>
class MyList

修改

以下是带有标准分配器的客户端代码:

//main.cpp
#include "MyList.h"

int main()
{
    MyList<int, std::allocator<Node<int>>> my_list;
    //auto my_list = MyList<int, std::allocator<int>>; // will not work
    for (int i = 0; i < 10; ++i)
    {
        my_list.emplace(i);
    }
    return 0;
}

1 个答案:

答案 0 :(得分:2)

这些是分配器的要求:http://en.cppreference.com/w/cpp/concept/Allocator

请注意,模板重绑定是可选的。

以下列出了容器必须具备的内容才能获得该概念。 http://en.cppreference.com/w/cpp/concept/AllocatorAwareContainer

是的,喘息。我徒劳地寻找一个简单的,或至少是极简主义的例子。如果您只需要一个链表,并且可以使用C ++ 11或更高版本,请使用std :: forward_list。

以下是给出的示例。

template<typename T, typename MyAllocator = std::allocator<T>>
class MyList
{
private:
    using node_alloc_t = typename std::allocator_traits<MyAllocator>::
        template rebind_alloc<Node<T>>;

    // create an object of type node allocator
    node_alloc_t node_alloc;
    // etc ....

public:

    template<typename T>
    void emplace(T v)
    {
        Node<T>* new_Node = node_alloc.allocate(1);

        // Etc...
    }

// etc...
};

现在一起......

#include <memory>
#include <iostream>

template<typename T>
struct Node
{

    Node() : m_next(nullptr) {}

    Node(T const &t) :
        m_value(t),
        m_next(nullptr)
    {}

    T m_value;
    Node<T>* m_next;
};

template<typename T, typename MyAllocator = std::allocator<T>>
class MyList
{
private:
    using node_alloc_t = typename std::allocator_traits<MyAllocator>::
        template rebind_alloc<Node<T>>;

    // create an object of type node allocator
    node_alloc_t node_alloc;

public:

    class Iterator
    {
    private:
        Node<T>* m_Node;
    public:

        Iterator(Node<T>* Node) : m_Node(Node) {};

        bool operator==(const Iterator& other)
        {
            return this == &other || m_Node == other.m_Node;
        }

        bool operator!=(const Iterator& other)
        {
            return !operator==(other);
        }

        T operator*()
        {
            if (m_Node)
            {
                return m_Node->m_value;
            }
            return T();
        }

        Iterator operator++()
        {
            Iterator i = *this;
            if (m_Node)
            {
                m_Node = m_Node->m_next;
            }
            return i;
        }

    };


    template<typename T>
    void emplace(T v)
    {

        Node<T>* new_Node = node_alloc.allocate(1);
        node_alloc.construct(new_Node, v);
        if (m_head)
        {
            m_tail->m_next = new_Node;
        }
        else {
            m_head = new_Node;
            new_Node->m_next = nullptr;
        }
        m_tail = new_Node;
    }

    Iterator begin() const
    {
        return Iterator(m_head);
    }


    Iterator end() const
    {
        return Iterator(nullptr);
    }

}; 

int main()
{   
    MyList<int> my_list; 
    for (int i = 0; i < 10; ++i)
    {
        my_list.emplace(i);
    }
    for (auto i : my_list) {
        std::cout << i << std::endl;
    }
    return 0;
}