繁体   English   中英

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

[英]Custom pointer types and container/allocator typedefs

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

typename std::vector<T>::pointer
typename std::vector<T>::const_pointer

用于创建typedef的实际指针类型是通过std::allocator_traits确定的

typedef typename std::allocator_traits<Allocator>::pointer pointer;

由于每个容器也有一个value_type类型定义中,想必目的pointer的typedef是一些奇怪的情况下使用的指针类型比其他东西value_type* 我从来没有亲自看到类似的用例,但我认为标准委员会希望提供使用容器的自定义指针类型的可能性。

问题是,这似乎与为std::allocator_traits的函数提供的定义不一致。 具体来说,在std::allocator_traits我们有construct函数,它被定义为:

template <class T, class... Args>
static void construct(Alloc& a, T* p, Args&&... args);

...只调用a.construct(p, std::forward<Args>(args)...)

但请注意,此函数不对自定义指针类型进行任何规定。 参数p是一个普通的本机指针。

那么,为什么这个函数的定义不是这样的:

template <class... Args>
static void construct(Alloc& a, typename Alloc::pointer p, Args&&... args);

似乎没有这个,使用std::allocator_traits<Alloc>::construct容器如果与定义一些自定义指针类型的Allocator一起使用将会失败。

那么,这里发生了什么? 或者我是否误解了首先使用pointer typedef的目的?

这种二分法是有目的的,并不存在问题。 construct成员函数通常如下所示实现:

template <class U, class ...Args>
void
construct(U* p, Args&& ...args)
{
    ::new(static_cast<void*>(p)) U(std::forward<Args>(args)...);
}

即它转发到new位置,这又有这个签名:

void* operator new  (std::size_t size, void* ptr) noexcept;

所以最终你需要一个“真正的”指针来调用新的位置。 并且为了传达需要构造的对象的类型,在指针类型(例如U* )中传递该信息是有意义的。

对于对称性, destroy也是根据实际指针来表达的,通常如下所示实现:

template <class U>
void
destroy(U* p)
{
    p->~U();
}

“花式指针”的主要用例是将对象放入共享内存中。 名为offset_ptr的类通常用于此目的,并且可以创建分配器以分配和释放offset_ptr引用的offset_ptr 因此allocator_traitsallocator根据pointer而不是value_type*allocatedeallocate函数流量。

所以问题出现了:如果你有一个pointer ,需要一个T* ,你会怎么做?

我知道有两种技术可以从pointer p创建T*

1. std::addressof(*p);

取消引用pointer p ,必须根据标准生成左值。 然而,能够放松这个要求会很好(例如,考虑一个返回代理引用的pointer例如vector<bool>::reference )。 指定std::addressofT*返回给任何左值:

template <class T> T* addressof(T& r) noexcept;

2. to_raw_pointer(p); // where: to_raw_pointer(p); // where:

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->());
}

这会调用pointeroperator->() ,它将直接返回T*或转发到直接或间接返回T* 所有pointer类型都应该支持operator->() ,即使它引用了bool 这种技术的缺点是当前operator->()需要不被调用,除非pointer是dereferenecable。 应该在标准中解除这一限制。

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


如果你有一个T*并希望构建一个pointer ,那你就不走运了。 没有可移植的方式来转换这个方向。


还要注意这个关于vector::data()成员函数的返回类型的切向相关的LWG问题 它在value_type*pointer和后退之间反弹,并且当前(并且有目的地)为value_type*

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM