Answers:
C ++ 11中的util.smartptr.shared.const / 9:
效果:构造一个拥有对象p和删除器d的shared_ptr对象。第二和第四构造函数应使用a的副本为内部使用分配内存。
第二和第四构造函数具有以下原型:
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);
在最新草案中,util.smartptr.shared.const / 10等效于我们的目的:
效果:构造一个拥有对象p和删除器d的shared_ptr对象。当T不是数组类型时,第一和第二个构造函数使用p启用shared_from_this。第二和第四构造函数应使用a的副本为内部使用分配内存。如果引发异常,则调用d(p)。
因此,如果需要在分配的内存中分配它,则使用分配器。根据当前标准和相关缺陷报告,分配不是强制性的,而是由委员会承担。
虽然界面shared_ptr
允许那里是从来没有一个控制模块和所有的实现shared_ptr
,并weak_ptr
放入一个链表,这两个东西实际上没有这样的实现。此外,措词已被修改,例如假设use_count
共享。
删除程序仅需移动可构造的。因此,不可能有多个副本shared_ptr
。
可以想像一种将删除程序放入经过特殊设计的实现中,并在删除特殊设置shared_ptr
时将其移动的实现shared_ptr
。尽管实现看起来是一致的,但也很奇怪,特别是因为使用计数可能需要一个控制块(使用计数来做相同的事情也许是可能的,但甚至很奇怪)。
相关DR的我发现:545,575,2434(其承认所有实现都使用控制块和似乎意味着多线程约束稍微授权的话),2802(这要求删除器仅移动constructible并因此防止实现,其中删除程序将在多个之间复制shared_ptr
)。
a
)来释放该内存的任何信息。这意味着将存储该副本a
。[util.smartptr.shared.dest]中没有有关此信息。
从std :: shared_ptr我们有:
控制块是一个动态分配的对象,它包含:
从std :: allocate_shared我们得到:
template< class T, class Alloc, class... Args >
shared_ptr<T> allocate_shared( const Alloc& alloc, Args&&... args );
构造类型为T的对象,并将其包装在std :: shared_ptr [...]中,以便对共享指针的控制块和T对象使用一种分配。
所以看起来std :: allocate_shared应该deleter
用Alloc
。
编辑:从n4810
§20.11.3.6创建[util.smartptr.shared.create]
1适用于所有的共同要求
make_shared
,allocate_shared
,make_shared_default_init
,和allocate_shared_default_init
过载,除非另有说明,在下面描述。[...]
7备注:(7.1)— 实现应执行不超过一个的内存分配。[注:这提供了等效于侵入式智能指针的效率。—尾注]
[强调所有我的]
因此,标准是说,std::allocate_shared
应该使用Alloc
的控制块。
n4810
并更新答案。
make_shared
不是构造函数本身。不过,我可以将成员用于小型删除器。
我相信这是未指定的。
以下是相关构造函数的规范:[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);
效果:构造一个
shared_ptr
拥有该对象p
和Deleter 的对象d
。当T
不是数组类型,第一和第二构造使shared_from_this
用p
。第二和第四构造函数应使用的副本a
为内部使用分配内存。如果引发异常,d(p)
则调用。
现在,我的解释是,当实现需要内存供内部使用时,可以使用来实现a
。这并不意味着实现必须使用此内存来放置所有内容。例如,假设有一个奇怪的实现:
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
}
// ...
};
此实现是否“使用的副本a
为内部使用分配内存”?是的,它确实。除了使用,它从不分配内存a
。这个幼稚的实现有很多问题,但可以说,除了最简单的情况外,它都转换为使用分配器shared_ptr
。关键是,仅仅因为我们无法想象一个有效的实现本身并不能证明它在理论上是不存在的。我并不是说这样的实现实际上可以在现实世界中找到,只是该标准似乎并未积极禁止它。
shared_ptr
for small type在堆栈上分配内存。因此不符合标准要求
std::move(__d)
,并退至需要allocate
复制时。