简体   繁体   English

存储在 memory 中的 shared_ptr 的删除器是否由自定义分配器分配?

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

Say I have a shared_ptr with a custom allocator and a custom deleter.假设我有一个带有自定义分配器自定义删除器的shared_ptr

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.我在标准中找不到任何关于删除器应该存储在哪里的内容:它没有说自定义分配器将用于删除器的 memory,也没有说不会

Is this unspecified or am I just missing something?这是未指定还是我只是错过了什么?

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

Effects: Constructs a shared_ptr object that owns the object p and the deleter d.效果:构造一个 shared_ptr object 拥有 object p 和删除器 d。 The second and fourth constructors shall use a copy of a to allocate memory for internal use.第二个和第四个构造函数要使用 a 的副本来分配 memory 供内部使用。

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:在最新的草案中, util.smartptr.shared.const/10 等效于我们的目的:

Effects: Constructs a shared_ptr object that owns the object p and the deleter d.效果:构造一个 shared_ptr object 拥有 object p 和删除器 d。 When T is not an array type, the first and second constructors enable shared_from_this with p.当 T 不是数组类型时,第一个和第二个构造函数启用 shared_from_this 和 p。 The second and fourth constructors shall use a copy of a to allocate memory for internal use.第二个和第四个构造函数要使用 a 的副本来分配 memory 供内部使用。 If an exception is thrown, d(p) is called.如果抛出异常,则调用 d(p)。

So the allocator is used if there is a need to allocate it in allocated memory.因此,如果需要在已分配的 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.虽然shared_ptr的接口允许实现永远没有控制块并且所有shared_ptrweak_ptr都放在一个链表中,但实际上并没有这样的实现。 Additionally, the wording has been modified assuming, for instance, that the use_count is shared.此外,已经修改了措辞,例如假设use_count是共享的。

  • The deleter is required to only move constructible.删除器只需要移动可构造的。 Thus, it is not possible to have several copies in the shared_ptr .因此,在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.可以想象一种实现,它将删除器放在专门设计的shared_ptr中,并在删除特殊的shared_ptr时移动它。 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).我发现的相关 DR: 5455752434 (它们承认所有实现都使用控制块,并且似乎暗示多线程约束在某种程度上要求它), 2802 (它要求删除器只能移动可构造,因此阻止实现删除器在几个shared_ptr之间复制)。

From std::shared_ptr we have:std::shared_ptr我们有:

The control block is a dynamically-allocated object that holds:控制块是一个动态分配的 object,它包含:

  • either a pointer to the managed object or the managed object itself;指向托管 object 或托管 object 本身的指针;
  • the deleter (type-erased);删除器(类型擦除);
  • the allocator (type-erased);分配器(类型擦除);
  • the number of shared_ptrs that own the managed object;拥有托管 object 的 shared_ptr 的数量;
  • the number of weak_ptrs that refer to the managed object.引用托管 object 的 weak_ptr 的数量。

And from std::allocate_shared we get:std::allocate_shared我们得到:

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.构造一个 T 类型的 object 并将其包装在 std::shared_ptr [...] 中,以便为共享指针的控制块和 T object 使用一个分配

So it looks like std::allocate_shared should allocate the deleter with your Alloc .所以看起来std::allocate_shared应该用你的Alloc分配deleter

EDIT: And from n4810 §20.11.3.6 Creation [util.smartptr.shared.create]编辑:从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. 1 除非另有说明,否则适用于所有make_sharedallocate_sharedmake_shared_default_initallocate_shared_default_init重载的通用要求如下所述。

[...] [...]

7 Remarks: (7.1) — Implementations should perform no more than one memory allocation. 7 备注: (7.1) —实现应该执行不超过一个 memory 分配。 [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.所以标准是说std::allocate_shared应该使用Alloc作为控制块。

I believe this is unspecified.我相信这是未指定的。

Here's the specification of the relevant constructors: [util.smartptr.shared.const]/10以下是相关构造函数的规范: [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 .效果:构造一个shared_ptr object 拥有 object p和删除器d When T is not an array type, the first and second constructors enable shared_from_this with p .T不是数组类型时,第一个和第二个构造函数启用shared_from_thisp The second and fourth constructors shall use a copy of a to allocate memory for internal use .第二个和第四个构造函数要使用a的副本来分配 memory 供内部使用 If an exception is thrown, d(p) is called.如果抛出异常,则调用d(p)

Now, my interpretation is that when the implementation needs memory for internal use, it does so by using a .现在,我的解释是,当实现需要 memory 供内部使用时,它通过a . It doesn't mean that the implementation has to use this memory to place everything.这并不意味着实现必须使用这个 memory 来放置所有东西。 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, std::move(__d));
        else
            // use 'a' to allocate storage for the deleter
    }
// ...
};

Does this implementation "use a copy of a to allocate memory for internal use"?此实现是否“使用a的副本分配 memory 供内部使用”? Yes, it does.是的,它确实。 It never allocates memory except by using a .除了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.这种幼稚的实现存在许多问题,但假设它切换到使用分配器,但最简单的情况除外,在最简单的情况下, shared_ptr是直接从指针构造的,从不复制、移动或以其他方式引用,并且没有其他复杂情况。 The point is, just because we fail to imagine a valid implementation doesn't by itself prove that it cannot theoretically exist.关键是,仅仅因为我们无法想象一个有效的实现本身并不能证明它在理论上不存在。 I am not saying that such an implementation can actually be found in the real world, just that the standard doesn't seem to be actively prohibiting it.我并不是说这样的实现实际上可以在现实世界中找到,只是标准似乎并没有积极禁止它。

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

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