我尝试创建一个使用智能指针的自定义内存分配器。我没有发布代码,因为它太大而且没有添加太多信息。然后我用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应满足CopyConstructible(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 reqs,他们会添加一些其他要求:
[17.6.3.3] NullablePointer类型是一种类似指针的类型,支持空值。如果出现以下情况,则类型P满足NullablePointer的要求:
的要求。
(1.1) - P满足EqualityComparable,DefaultConstructible,CopyConstructible,CopyAssignable和Destructible ...
如果我检查随机访问迭代器的要求,我也没有发现任何明确提到它的转换为原始指针。但在少数地方使用addressof
的方法(例如24.2.1-5)。
此外,它并不是Microsoft std::vector
实现中唯一一个假定X::pointer
和原始指针相等的地方。我想知道,我错过了什么?
编辑:我将在这里添加一段my_allocator deffinition:
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);
};
答案 0 :(得分:13)
为了解决这个问题,我创建了一个to_raw_pointer
函数,它恰好适用于任何&#34;花式指针&#34;它实现了operator->()
。您可以在libc++ implementation。
这是:
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
等内容。
容器还需要通过construct
调用allocator_traits
,而不是直接在存储的分配器上调用它,如图所示。这是为了让construct
成为默认&#34;由allocator_traits
提出,而不是要求分配者实施construct
。
目前,operator*()
和operator->()
通常都要求花式指针在调用该运算符之前为非空。但是我预计将来operator->()
将放宽此要求。
<强>更新强>
当我写上述内容时,我有点匆忙。现在我有时间,我将在allocator::pointer
类型中包含完整的要求。然而,在重新阅读这个问题时,我发现Maxym已经在问题中做得很好,所以我不会在这里重复这些问题。
std中的一件事,但并不完全明显,是四种指针类型之间的隐式和显式转换:pointer
,const_pointer
,void_pointer
和{ {1}}:
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
或const_cast
的容器无法const
(抛弃allocator::const_pointer
- ness)。一旦容器进入allocator::const_void_pointer
,它就永远无法返回。