C++ 自定义指针类型和容器/分配器类型定义

C++ 自定义指针类型和容器/分配器类型定义,c++,c++11,C++,C++11,C++标准容器和分配器为容器使用的指针类型提供typedef,即: typename std::vector<T>::pointer typename std::vector<T>::const_pointer 由于每个容器也有一个value\u类型typedef,因此指针typedef的用途大概是用于某些奇怪的情况,其中使用的指针类型不是value\u类型*。我个人从未见过这样的用例,但我想标准委员会希望提供在容器中使用自定义指针类型的可能性 问题在于,这似乎与std

C++标准容器和分配器为容器使用的指针类型提供typedef,即:

typename std::vector<T>::pointer
typename std::vector<T>::const_pointer
由于每个容器也有一个
value\u类型
typedef,因此
指针
typedef的用途大概是用于某些奇怪的情况,其中使用的指针类型不是
value\u类型*
。我个人从未见过这样的用例,但我想标准委员会希望提供在容器中使用自定义指针类型的可能性

问题在于,这似乎与
std::allocator\u traits
中为函数提供的定义不一致。具体地说,在
std::allocator\u traits
中,我们有
构造
函数,其定义如下:

template <class T, class... Args>
static void construct(Alloc& a, T* p, Args&&... args);
如果没有这一点,使用
std::allocator\u traits::construct
的容器如果与定义某些自定义指针类型的分配器一起使用,则可能会失败


那么,这是怎么回事?或者我误解了使用
指针
typedef的初衷吗?

这种二分法是有目的的,不会产生问题。
construct
成员函数通常是这样实现的:

template <class U, class ...Args>
void
construct(U* p, Args&& ...args)
{
    ::new(static_cast<void*>(p)) U(std::forward<Args>(args)...);
}
因此,最终您需要一个“真正的”指针来调用新的。为了传递需要构造的对象的类型,只需在指针类型中传递该信息(例如,
U*

对于对称性,
destroy
也用实际指针表示,通常实现如下:

template <class U>
void
destroy(U* p)
{
    p->~U();
}
2.
到原始指针(p);//其中:

template <class T>
inline
T*
to_raw_pointer(T* p) noexcept
{
    return p;
}

template <class Pointer>
inline
typename std::pointer_traits<Pointer>::element_type*
to_raw_pointer(Pointer p) noexcept
{
    return ::to_raw_pointer(p.operator->());
}
模板
内联
T*
到原始指针(T*p)无异常
{
返回p;
}
模板
内联
typename std::指针\特征::元素\类型*
to_原始_指针(指针p)无异常
{
return::to_raw_指针(p.operator->());
}
这将调用
指针
运算符->()
,它将直接返回
T*
,或转发到直接或间接返回
T*
的对象。所有
指针
类型都应支持
运算符->()
,即使它引用的是
布尔值
。这种技术的一个缺点是,除非
指针
是可撤销的,否则当前不需要调用
操作符->()
。该标准应取消这一限制

在C++14中,第二个重载的返回类型(实际上是两个重载)可以方便地替换为
auto


如果您有一个
T*
并希望构造一个
指针,那么您就运气不好了。在这个方向上没有可移植的转换方式



还要注意
vector::data()
成员函数的返回类型。它在
value\u-type*
指针
之间来回跳转,并且当前(有目的地)将
value\u-type*
构造
固定到元素类型。同样地,
销毁
<代码>分配
取消分配
不可用。
allocate
deallocate
都指向您正在检查的
指针类型。这两个函数都不处理实际的对象构造和破坏,因此不保证固定到类型
T
。然而,
构造
销毁
,都是如此。@dyp
::std::addressof(*p)
,而不是;)
pointer!=值类型*
似乎已经(某种程度上)在C++11中引入。据我所知,在C++03中,容器实现者可以假定
指针==value\u type*
。我目前正在搜索关于它为什么被改变的建议。这似乎与分配器概念文件(N2654)和可能的作用域分配器模型(N2554)有关。在重新阅读这些文件后,似乎删除了
指针==value\u type*
的限制。这可能与使用smart/fancy指针有关,例如用于共享内存分配器。我的意思是,在标准中应该放宽这一要求。谢谢,我将澄清我粗心的语言。“因此,最终您需要一个“真正的”指针来调用placement new。”但毫无疑问,分配器应该知道如何从
指针中获取
T*
。。那么,为什么不由分配器负责进行转换/提取呢?这对分配器的API来说是一个不必要的补充。事实上,C++98/03分配器实际上在
address
成员函数中有这样一个API,而这一要求只是为了更有用的独立实用程序
std::addressof()
而放弃了。在我对OP的评论中,我推测了指针算法。据我所见,
指针
不需要支持指针算法,因此除了数组的第一个位置之外,不可能在任何位置构造元素-正如您所说,不可能从
T*
转换回
指针
。这可能是另一个原因吗?@dyp:不,我不这么认为。[allocator.requirements]/p5部分说:
X::pointer
X::const_pointer
也应满足随机访问迭代器(24.2)的要求。这需要指针运算。
void* operator new  (std::size_t size, void* ptr) noexcept;
template <class U>
void
destroy(U* p)
{
    p->~U();
}
template <class T> T* addressof(T& r) noexcept;
template <class T>
inline
T*
to_raw_pointer(T* p) noexcept
{
    return p;
}

template <class Pointer>
inline
typename std::pointer_traits<Pointer>::element_type*
to_raw_pointer(Pointer p) noexcept
{
    return ::to_raw_pointer(p.operator->());
}