Reputation: 63359
From https://en.cppreference.com/w/cpp/memory/shared_ptr/allocate_shared:
template< class T, class Alloc, class... Args > shared_ptr<T> allocate_shared( const Alloc& alloc, Args&&... args );
The storage is typically larger than
sizeof(T)
in order to use one allocation for both the control block of the shared pointer and theT
object. ... All memory allocation is done using a copy ofalloc
, which must satisfy the Allocator requirements.
What type is then used to allocate the aforementioned storage? In other words, what should be Alloc::value_type
, one of the Allocator requirements?
Upvotes: 4
Views: 1471
Reputation: 26302
The actual type used depends on the implementation. By Allocator requirements and with the help of std::allocator_traits
traits class template, any allocator can be rebind
ed to another type via std::allocator_traits<A>::rebind_alloc<T>
mechanism.
Suppose you have an allocator
template<class T>
class MyAlloc { ... };
If you write:
std::allocate_shared<T>(MyAlloc<T>{});
it doesn't mean that MyAlloc<T>
will be used to perform allocations. If internally we need to allocate an object of another type S
, we can get an appropriate allocator via
std::allocator_traits<MyAlloc<T>>::rebind_alloc<S>
It is defined such that if MyAlloc
itself doesn't provide rebind<U>::other
member type alias, the default implementation is used, which returns MyAlloc<S>
. An allocator object is constructed from that passed to std::allocate_shared()
(here, MyAlloc<T>{}
) by an appropriate MyAlloc<S>
's converting constuctor (see Allocator requirements).
Let's take a look at some particular implementation - libstdc++. For the line above, the actual allocation is performed by
MyAlloc<std::_Sp_counted_ptr_inplace<T, Alloc<T>, (__gnu_cxx::_Lock_policy)2>
which is reasonable: std::allocate_shared()
allocates memory for an object that contains both T
and a control block. _Sp_counted_ptr_inplace<T, ...>
is such an object. It holds T
inside itself in the _M_storage
data member:
__gnu_cxx::__aligned_buffer<T> _M_storage;
The same mechanism is used in many other places. For example, std::list<T, Alloc>
employs it to obtain an allocator that is then used to allocate list nodes, which in addition to T
hold pointers to their neighbours.
An interesting related question is why allocator is not a template template parameter, which might seem a natural choice. It is discussed here.
Upvotes: 3
Reputation: 76688
It doesn't matter. The type must only satisfy the allocator requirements ([util.smartptr.shared.create]/2, also in C++11).
One of the requirements is that for every (cv-unqualified) object type U
, Alloc::rebind<U>::other
yields the corresponding allocator type for a value_type
of U
(or if this is not implemented directly in the allocator type that allocator_traits
can provide a default implementation by replacing the (first) template argument of the class template that Alloc
is a specialization of.)
In this way, no matter for what value_type
your passed allocator is, std::allocate_shared
can just obtain an allocator to the (unspecified) type it requires for combined storage by rebinding.
For example, with
struct MyType {};
int main() {
std::cout << "MyType size is " << sizeof(MyType) << "\n";
allocator<MyType> a;
auto x = std::allocate_shared<MyType>(a);
}
where allocator
is a minimal allocator implementation printing details about every allocation call via typeid
, I get for current GCC with libstdc++:
MyType size is 1
Allocating for 1 objects of type St23_Sp_counted_ptr_inplaceI6MyType9allocatorIS0_ELN9__gnu_cxx12_Lock_policyE2EE
with size 24 each.
and with current Clang and libc++:
MyType size is 1
Allocating for 1 objects of type NSt3__120__shared_ptr_emplaceI6MyType9allocatorIS1_EEE
with size 32 each.
https://godbolt.org/z/bPqeYTP7Y
Upvotes: 1
Reputation: 238351
what should be Alloc::value_type
The requirement of allocators is that Alloc::value_type
is same as the T
in the Allocator requirements - not to be confused with T
of allocate_shared
. So, for example, indirecting through Alloc::pointer
must yield a value of type Alloc::value_type&
.
There is no requirement which Alloc::value_type
needs to be for std::allocate_shared
as long as it conforms to Allocator requirements.
What type is used by std::allocate_shared to allocate memory?
std::allocate_shared
will use a copy of alloc
rebound to an unspecified value_type
.
Upvotes: 0