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

How to use rebind with custom allocator and custom list

本文关键字:自定义 分配器 列表 一起 新绑定 绑定      更新时间:2023-10-16

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

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*"转换为"节点*">

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

编辑

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

//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;
}

以下是分配器的要求: 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;
}