默认情况下,`std::shared_ptr` 不应该使用 `std::default_delete` 吗?

Shouldn't `std::shared_ptr` use `std::default_delete` by default?

std::default_delete can be specialized to allow std::unique_ptrs to painlessly manage types which have to be destroyed by calling some custom destroy-function instead of using delete p;.

在 C++ 中,基本上有两种方法可以确保对象由 std::shared_ptr 管理:

  1. 创建它由一个共享指针管理,使用剩余的 std::make_shared or std::allocate_shared. This is the preferred way, as it coalesces both memory-blocks needed (payload and reference-counts) into one. Though iff there are only std::weak_ptrs,对引用计数的需求也必然会为有效负载占用内存。

  2. 之后使用 a constructor or .reset().

  3. 将管理分配给共享指针

第二种情况,当不提供自定义删除器时很有趣:

具体来说,它被定义为使用自己的未指定类型的删除器,该删除器分别使用 delete [] p;delete p;,具体取决于是否为数组实例化 std::shared_ptr

引自 n4659 (~C++17):

template<class Y> explicit shared_ptr(Y* p);

4 Requires: Y shall be a complete type. The expression delete[] p, when T is an array type, or delete p, when T is not an array type, shall have well-defined behavior, and shall not throw exceptions.
5 Effects: When T is not an Array type, constructs a shared_ptr object that owns the pointer p. Otherwise, constructs a shared_ptr that owns p and a deleter of an unspecified type that calls delete[] p. When T is not an array type, enables shared_from_this with p. If an exception is thrown, delete p is called when T is not an array type, delete[] p otherwise.
6 Postconditions: use_count() == 1 && get() == p.
[…]

template<class Y> void reset(Y* p);

3 Effects: Equivalent to shared_ptr(p).swap(*this).

我的问题是:

  1. 有没有最好的理由不指定使用 std::default_delete 来代替?
  2. 该更改是否会破坏任何有效(并且可能有用?)代码?
  3. 是否已经有人提议这样做了?

Is there a, preferably good, reason that it is not specified to use std::default_delete instead?

因为它不会做你想做的事。看,仅仅因为你可以专注于某些东西并不意味着你可以劫持它。标准说 ([namespace.std]):

A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.

std::default_delete<T>::operator()(T* ptr) 行为的标准库要求是它 "Calls delete on ptr." 所以你对它的专业化 必须 做同样的事情。

因此,让 shared_ptr 执行 delete ptr; 和让 shared_ptr 调用 default_delete<T>{}(ptr).

应该没有区别

这就是为什么 unique_ptr 采用删除器类型,而不是依靠您来专门化它。


来自评论:

The specialization deletes the object, in the only proper way.

但这不是要求所说的。它说的是 "Calls delete on ptr." 它并没有说 "ends the lifetime of the object pointed to by ptr" 或 "destroys the object referenced by ptr" 这样更含糊的东西。它给出了必须发生的显式代码。

而且你的专业化必须跟进。

如果您仍然不相信,论文 P0722R1 是这样说的:

Note that the standard requires specializations of default_delete<T> to have the same effect as calling delete p;,

很明显,作者同意专业化 default_delete 不是添加您自己的行为的机制。

所以你提问的前提是不成立的


但是,让我们暂时假设您的问题是有效的,这样的专业化是可行的。不管有效与否,专门 default_delete 来自定义删除器行为并不是 预期的 这样做的方法。如果这是意图,则根本不需要 unique_ptr 的删除器对象。最多,您只需要一个参数来告诉您指针类型是什么,默认为 T*.

所以这是不这样做的一个很好的理由。