Max*_*xym 12 c++ pointers memory-management standard-library c++11
我试图创建一个使用智能指针的自定义内存分配器.我没有发布代码,因为它太大而且没有添加太多信息.然后我用一个测试它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]));
....
}
Run Code Online (Sandbox Code Playgroud)
错误消息有点令人困惑.真正的问题是this->_Mylast类型my_allocator< int >::pointer,它是一个智能指针,而构造方法是期望的int*.
所以,问题很简单:自定义内存分配器中使用的指针类型有哪些要求?应该X::pointer可以转换为原始指针?如果是的话,它会让它们变得毫无用处.
实际上我希望这行代码看起来像:
this->_Getal().construct(addressof(*(this->_Mylast)),
_STD forward<value_type>(this->_Myfirst[_Idx]));
Run Code Online (Sandbox Code Playgroud)
让我们尝试在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);
};
Run Code Online (Sandbox Code Playgroud)
How*_*ant 13
为了解决这个问题,我创建了一个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->());
}
Run Code Online (Sandbox Code Playgroud)
它通过operator->()以非常规方式调用它来工作.此运算符必须调用另一个operator->(),或返回一个真正的指针.实指针的重载会使用标识函数打破递归.所以这将被用作:
this->_Getal().construct(__to_raw_pointer(this->_Mylast),
_STD forward<value_type>(this->_Myfirst[_Idx]));
Run Code Online (Sandbox Code Playgroud)
construct被指定采用真正的指针,而不是花哨的指针.并且没有从指向真实指针的花式指针指定的隐式转换.容器必须使用诸如to_raw_pointer或之类的东西addressof.
容器也需要调用constructvia allocator_traits,而不是直接在存储的分配器上调用它,如图所示.这是为了允许construct"默认" allocator_traits,而不是要求分配器实现construct.
目前operator*(),operator->()在调用该运算符之前,它们通常都要求花式指针为非null.但是我预计这个要求将来会放宽operator->().
更新
当我写上面的内容时,我有点匆忙.现在我有时间,我将包括对allocator::pointer类型的完整要求.然而,在重新阅读这个问题时,我发现Maxym已经在问题中做得很好,所以我在此不再赘述.
有一件事是性病,但不完全是显而易见的,是四个指针类型之间的隐性和显性的转换: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 |
+--------------------------------------+
Run Code Online (Sandbox Code Playgroud)
也就是说,你可以隐式地从非转换const到const,并从非void到void了,你可以明确地从转换void到非void.但是没有办法让一个容器const_cast(抛弃const)来自一个allocator::const_pointer或者allocator::const_void_pointer.一旦容器运行const,它就永远不会回来.