Petr
Petr

Reputation: 63359

What type is used by std::allocate_shared to allocate memory?

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 the T object. ... All memory allocation is done using a copy of alloc, 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

Answers (3)

Evg
Evg

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 rebinded 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

user17732522
user17732522

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

eerorika
eerorika

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

Related Questions