默认情况下,`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_ptr
s to painlessly manage types which have to be destroyed by calling some custom destroy-function instead of using delete p;
.
在 C++ 中,基本上有两种方法可以确保对象由 std::shared_ptr
管理:
创建它由一个共享指针管理,使用剩余的 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_ptr
s,对引用计数的需求也必然会为有效负载占用内存。
之后使用 a constructor or .reset()
.
将管理分配给共享指针
第二种情况,当不提供自定义删除器时很有趣:
具体来说,它被定义为使用自己的未指定类型的删除器,该删除器分别使用 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)
.
我的问题是:
- 有没有最好的理由不指定使用
std::default_delete
来代替?
- 该更改是否会破坏任何有效(并且可能有用?)代码?
- 是否已经有人提议这样做了?
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*
.
所以这是不这样做的一个很好的理由。
std::default_delete
can be specialized to allow std::unique_ptr
s to painlessly manage types which have to be destroyed by calling some custom destroy-function instead of using delete p;
.
在 C++ 中,基本上有两种方法可以确保对象由 std::shared_ptr
管理:
创建它由一个共享指针管理,使用剩余的
std::make_shared
orstd::allocate_shared
. This is the preferred way, as it coalesces both memory-blocks needed (payload and reference-counts) into one. Though iff there are onlystd::weak_ptr
s,对引用计数的需求也必然会为有效负载占用内存。之后使用 a constructor or
.reset()
. 将管理分配给共享指针
第二种情况,当不提供自定义删除器时很有趣:
具体来说,它被定义为使用自己的未指定类型的删除器,该删除器分别使用 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 expressiondelete[] p
, whenT
is an array type, ordelete p
, whenT
is not an array type, shall have well-defined behavior, and shall not throw exceptions.
5 Effects: WhenT
is not an Array type, constructs ashared_ptr
object that owns the pointerp
. Otherwise, constructs ashared_ptr
that ownsp
and a deleter of an unspecified type that callsdelete[] p
. WhenT
is not an array type, enablesshared_from_this
withp
. If an exception is thrown,delete p
is called whenT
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)
.
我的问题是:
- 有没有最好的理由不指定使用
std::default_delete
来代替? - 该更改是否会破坏任何有效(并且可能有用?)代码?
- 是否已经有人提议这样做了?
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 callingdelete p;
,
很明显,作者同意专业化 default_delete
不是添加您自己的行为的机制。
所以你提问的前提是不成立的
但是,让我们暂时假设您的问题是有效的,这样的专业化是可行的。不管有效与否,专门 default_delete
来自定义删除器行为并不是 预期的 这样做的方法。如果这是意图,则根本不需要 unique_ptr
的删除器对象。最多,您只需要一个参数来告诉您指针类型是什么,默认为 T*
.
所以这是不这样做的一个很好的理由。