具有自定义指针类型的内存分配器
Memory allocator with custom pointer type
我试图创建一个使用智能指针的自定义内存分配器。我不发布代码,因为它太大并且没有添加太多信息。然后我用std::vector
测试了它.它在Xcode上运行良好。但是当我尝试在Visual Studio 12(2013)中构建相同的代码时,构建失败并显示以下错误:
。vector(873): 错误 C2660: '
std::_Wrap_alloc< my_allocator< int > >::construct
' : 函数不接受 2 个参数
问题出在push_back方法上:
void push_back(value_type&& _Val)
{
....
this->_Getal().construct(this->_Mylast,
_STD forward<value_type>(this->_Myfirst[_Idx]));
....
}
错误消息有点令人困惑。真正的问题是this->_Mylast
是my_allocator< int >::pointer
类型,这是一个智能指针,构造方法期望int*
。
因此,问题很简单:自定义内存分配器中使用的指针类型有哪些要求?X::pointer
应该转换为原始指针吗?如果是,这会使它们变得毫无用处。
实际上,我希望这行代码如下所示:
this->_Getal().construct(addressof(*(this->_Mylast)),
_STD forward<value_type>(this->_Myfirst[_Idx]));
让我们尝试在C++标准中找到答案,它说:
[17.6.3.5-5] X型分配器必须满足可复制可构造(17.6.3.1)的要求。
X::pointer
、X::const_pointer
、X::void_pointer
和X::const_void_pointer
类型应满足 NullablePointer (17.6.3.3) 的要求。这些类型的构造函数、比较运算符、复制操作、移动操作或交换操作都不应通过异常退出。X::pointer
和X::const_pointer
还应满足随机访问迭代器的要求 (24.2)
如果我们看一下 NullablePointer 请求,它们增加了一些其他要求:
[17.6.3.3] NullablePointer 类型是支持 null 值的类似指针的类型。在以下情况下,P 类型满足 NullablePointer 的要求:
(1.1) — P 满足 EqualComparable、DefaultConstructible、CopyConstructible、CopyAssignable 和 Destructible 的要求。
如果我检查随机访问迭代器要求,我也没有发现任何明确提及其对原始指针的强制转换。但在少数地方使用addressof
的方法(例如24.2.1-5)。
此外,它并不是 Microsoft std::vector
实现中唯一假定 X::pointer
和原始指针相等的地方。我想知道,我错过了什么?
编辑:我将在这里添加一段my_allocator定义:
class my_allocator
{
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef my_ptr<T> pointer;
typedef my_ptr<const T> const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;
typedef my_ptr<void> void_pointer;
typedef my_ptr<const void> const_void_pointer;
<constructors>
pointer allocate(size_type n, const_void_pointer = nullptr);
void deallocate(const pointer& ptr, size_type elements_num);
};
为了解决这个问题,我创建了一个to_raw_pointer
函数,它恰好适用于任何实现operator->()
的"花哨指针"。 你可以在libc++实现中找到它。
在这里:
template <class _Tp>
inline _LIBCPP_INLINE_VISIBILITY
_Tp*
__to_raw_pointer(_Tp* __p) _NOEXCEPT
{
return __p;
}
template <class _Pointer>
inline _LIBCPP_INLINE_VISIBILITY
typename pointer_traits<_Pointer>::element_type*
__to_raw_pointer(_Pointer __p) _NOEXCEPT
{
return _VSTD::__to_raw_pointer(__p.operator->());
}
它的工作原理是以非常规的方式调用operator->()
。 这个运算符必须调用另一个operator->()
,或者返回一个真正的指针。 实指针的重载会使用恒等函数破坏递归。 所以这将像这样使用:
this->_Getal().construct(__to_raw_pointer(this->_Mylast),
_STD forward<value_type>(this->_Myfirst[_Idx]));
construct
被指定为采用真正的指针,而不是花哨的指针。 并且没有指定从花哨指针到真实指针的隐式转换。 容器必须使用 to_raw_pointer
或 addressof
之类的东西。
容器还需要通过allocator_traits
调用construct
,而不是如图所示直接在存储的分配器上调用它。 这是为了允许construct
被allocator_traits
"默认",而不是要求分配器实现construct
。
目前,operator*()
和 operator->()
通常都要求花哨的指针在调用该运算符之前为非空。 但是,我预计将来operator->()
将放宽这一要求。
更新
当我写上面的时候,我有点着急。 现在我有时间了,我将包括allocator::pointer
类型的完整要求。 然而,在重新阅读这个问题时,我发现Maxym在这个问题中已经做得很好,所以我不会在这里重复它们。
在 std 中但不完全明显的一件事是四种指针类型之间的隐式和显式转换:pointer
、const_pointer
、void_pointer
和 const_void_pointer
:
implicit allocator pointer conversions:
+--------------------------------------+
| pointer --> const_pointer |
| | | |
| | --------- | |
| |/ _| |/ |
| void_pointer --> const_void_pointer |
+--------------------------------------+
explicit allocator pointer conversions:
+--------------------------------------+
| pointer const_pointer |
| /| /| |
| | | |
| | | |
| void_pointer const_void_pointer |
+--------------------------------------+
也就是说,你可以从非const
隐式转换为const
,以及从非void
转换为void
,并且可以显式地从void
转换为非void
。 但是容器无法从allocator::const_pointer
或allocator::const_void_pointer
中const_cast
(丢弃const
-ness)。 一旦容器const
,就再也回不来了。
- 基于浅树的数据结构的内存分配器,用于频繁分配和解除分配
- 使用 AMD 的 Vulkan 内存分配器时出现链接器错误 (LNK2005)
- 自定义内存分配器示例(需要一些说明)
- 跨共享/静态库集成C++自定义内存分配器
- 带有realloc的自定义池内存分配器
- 正在重写MSVC++中的内存分配器
- 如何使用 CUDA 推力执行策略覆盖推力的低级设备内存分配器
- STL映射的自定义内存分配器
- 是否有自定义内存分配器设计模式不在其分配中存储元数据
- C++内存分配器和多态类型
- 具有自定义指针类型的内存分配器
- 符合 STL 标准的内存分配器库
- 内存分配器作用域的设计
- C++内存分配器体系结构
- 如何评价自定义内存分配器的质量
- 匿名段上的进程间内存分配器
- 处理使用boost::进程间STL兼容共享内存分配器创建的c++对象的正确方法是什么?
- c++内存分配器vptr/new实现
- 自定义内存分配器:T*指针,运算符new与void指针强制转换
- 是否有用于多线程内存分配器的验证套件