Is a shared_ptr's deleter stored in memory allocated by the custom allocator?

自闭症网瘾萝莉.ら 提交于 2019-12-06 16:57:05

问题


Say I have a shared_ptr with a custom allocator and a custom deleter.

I can't find anything in the standard that talks about where the deleter should be stored: it doesn't say that the custom allocator will be used for the deleter's memory, and it doesn't say that it won't be.

Is this unspecified or am I just missing something?


回答1:


util.smartptr.shared.const/9 in C++ 11:

Effects: Constructs a shared_ptr object that owns the object p and the deleter d. The second and fourth constructors shall use a copy of a to allocate memory for internal use.

The second and fourth constructors have these prototypes:

template<class Y, class D, class A> shared_ptr(Y* p, D d, A a);
template<class D, class A> shared_ptr(nullptr_t p, D d, A a);

In the latest draft, util.smartptr.shared.const/10 is equivalent for our purpose:

Effects: Constructs a shared_­ptr object that owns the object p and the deleter d. When T is not an array type, the first and second constructors enable shared_­from_­this with p. The second and fourth constructors shall use a copy of a to allocate memory for internal use. If an exception is thrown, d(p) is called.

So the allocator is used if there is a need to allocate it in allocated memory. Based on the current standard and at relevant defect reports, allocation is not mandatory but assumed by the committee.

  • Although the interface of shared_ptr allows an implementation where there is never a control block and all shared_ptr and weak_ptr are put in a linked list, there is no such implementation in practice. Additionally, the wording has been modified assuming, for instance, that the use_count is shared.

  • The deleter is required to only move constructible. Thus, it is not possible to have several copies in the shared_ptr.

One can imagine an implementation which puts the deleter in a specially designed shared_ptr and moves it when it the special shared_ptr is deleted. While the implementation seems conformant, it is also strange, especially since a control block may be needed for the use count (it is perhaps possible but even weirder to do the same thing with the use count).

Relevant DRs I found: 545, 575, 2434 (which acknowledge that all implementations are using a control block and seem to imply that multi-threading constraints somewhat mandate it), 2802 (which requires that the deleter only move constructible and thus prevents implementation where the deleter is copied between several shared_ptr's).




回答2:


From std::shared_ptr we have:

The control block is a dynamically-allocated object that holds:

  • either a pointer to the managed object or the managed object itself;
  • the deleter (type-erased);
  • the allocator (type-erased);
  • the number of shared_ptrs that own the managed object;
  • the number of weak_ptrs that refer to the managed object.

And from std::allocate_shared we get:

template< class T, class Alloc, class... Args >
shared_ptr<T> allocate_shared( const Alloc& alloc, Args&&... args );

Constructs an object of type T and wraps it in a std::shared_ptr [...] in order to use one allocation for both the control block of the shared pointer and the T object.

So it looks like std::allocate_shared should allocate the deleter with your Alloc.

EDIT: And from n4810 §20.11.3.6 Creation [util.smartptr.shared.create]

1 The common requirements that apply to all make_shared, allocate_shared, make_shared_default_init, and allocate_shared_default_init overloads, unless specified otherwise, are described below.

[...]

7 Remarks: (7.1) — Implementations should perform no more than one memory allocation. [Note: This provides efficiency equivalent to an intrusive smart pointer. —end note]

[Emphasis all mine]

So the standard is saying that std::allocate_shared should use Alloc for the control block.




回答3:


I believe this is unspecified.

Here's how the relevant constructors are specified: [util.smartptr.shared.const]/10

template<class Y, class D> shared_ptr(Y* p, D d);
template<class Y, class D, class A> shared_ptr(Y* p, D d, A a);
template <class D> shared_ptr(nullptr_t p, D d);
template <class D, class A> shared_ptr(nullptr_t p, D d, A a);

Effects: Constructs a shared_­ptr object that owns the object p and the deleter d. When T is not an array type, the first and second constructors enable shared_­from_­this with p. The second and fourth constructors shall use a copy of a to allocate memory for internal use. If an exception is thrown, d(p) is called.

Now, my interpretation is that when the implementation needs memory for internal use, it does so by using a. It doesn't mean that the implementation has to use this memory to place everything. For example, suppose that there's this weird implementation:

template <typename T>
class shared_ptr : /* ... */ {
    // ...
    std::aligned_storage<16> _Small_deleter;
    // ...
public:
    // ...
    template <class _D, class _A>
    shared_ptr(nullptr_t, _D __d, _A __a) // for example
        : _Allocator_base{__a}
    {
        if constexpr (sizeof(_D) <= 16)
            _Construct_at(&_Small_deleter, __d);
        else
            // use 'a' to allocate storage for the deleter
    }
};

Does this implementation "use a copy of a to allocate memory for internal use"? Yes, it does. It never allocates memory except by using a. There are many problems with this naive implementation, but let's say that it switches to using allocators in all but the simplest case in which the shared_ptr is constructed directly from a pointer and is never copied or moved or otherwise referenced and there are no other complications. The point is, just because we fail to imagine a valid implementation doesn't mean it can theoretically exist. I am not saying that such an implementation can actually be found, just that the standard doesn't seem to be actively prohibiting it.



来源:https://stackoverflow.com/questions/58932991/is-a-shared-ptrs-deleter-stored-in-memory-allocated-by-the-custom-allocator

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!