C++ standard containers and allocators provide typedefs for the pointer type used by the container, i.e.:
typename std::vector<T>::pointer
typename std::vector<T>::const_pointer
The actual pointer type used to create the typedef is determined via std::allocator_traits
typedef typename std::allocator_traits<Allocator>::pointer pointer;
Since each container also has a value_type
typedef, presumably the purpose of the pointer
typedef is for some weird situation where the pointer type used is something other than value_type*
. I've never personally seen a use case for something like that, but I suppose the standards committee wanted to provide the possibility of using custom pointer types with containers.
The problem is that this seems to be inconsistent with the definitions provided for the functions in std::allocator_traits
. Specifically, in std::allocator_traits
we have the construct
function, which is defined as:
template <class T, class... Args>
static void construct(Alloc& a, T* p, Args&&... args);
...which just calls a.construct(p, std::forward<Args>(args)...)
But note that this function makes no provisions for a custom pointer type. The parameter p
is a plain-old native pointer.
So, why isn't the definition of this function something like:
template <class... Args>
static void construct(Alloc& a, typename Alloc::pointer p, Args&&... args);
It seems without this, containers which used std::allocator_traits<Alloc>::construct
would fail if used with an Allocator that defines some custom pointer type.
So, what's going on here? Or am I misunderstanding the purpose of having pointer
typedefs in the first place?
This dichotomy is purposeful, and does not present a problem. The
construct
member function is typically implemented like this:I.e. it forwards to placement
new
, which in turn has this signature:So ultimately you need a "real" pointer to call placement new. And to convey the type of the object that needs to be constructed, it just makes sense to pass that information along in the pointer type (e.g.
U*
).For symmetry,
destroy
is also formulated in terms of an actual pointer and is typically implemented like so:The main use case for the "fancy pointer" is to place objects into shared memory. A class called
offset_ptr
is typically used for this purpose, and an allocator can be created to allocate and deallocate memory referred to byoffset_ptr
. And thus theallocator_traits
andallocator
allocate
anddeallocate
functions traffic in terms ofpointer
instead ofvalue_type*
.So the question arises: If you've got a
pointer
, and need aT*
, what do you do?There are two techniques I'm aware of for creating a
T*
from apointer p
1.
std::addressof(*p);
When you dereference a
pointer p
, it must result in an lvalue according to the standard. However it would be nice to be able to relax this requirement (e.g. consider apointer
returning a proxy reference such asvector<bool>::reference
).std::addressof
is specified to to return aT*
to any lvalue:2.
to_raw_pointer(p); // where:
This calls a
pointer
'soperator->()
, which will either directly return aT*
or forward to something that will either directly or indirectly return aT*
. Allpointer
types should supportoperator->()
, even if it is referencing abool
. A downside of this technique is that currentlyoperator->()
is required to not be called unless thepointer
is dereferenecable. That restriction should be lifted in the standard.In C++14 the return type of the second overload (well actually both overloads), can be conveniently replaced with
auto
.If you have a
T*
and wish to construct apointer
, you are out of luck. There is no portable way to convert in this direction.Also note this tangentially related LWG issue about the
vector::data()
member function's return type. It has bounced betweenvalue_type*
,pointer
and back, and is currently (and purposefully)value_type*
.