前言
继续看stl中常用东西的源码,打算把智能指针的坑给填了,这次先写点unique_prt,下次写shared_ptr。unique_prt的代码总体而言比较简单,唯一稍微复杂的地方就是对自定义析构函数的萃取。
STL版本
本文所使用的stl版本为libc++ 13.0,属于LLVM项目。
如果遇到不熟悉的宏定义可以参考文档Symbol Visibility Macros
Unique_ptr
依照惯例,来看一下定义的模板参数和一些typedef。
template <class _Tp, class _Dp = default_delete<_Tp> >
class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr {
public:
typedef _Tp element_type;
typedef _Dp deleter_type;
typedef _LIBCPP_NODEBUG_TYPE typename __pointer<_Tp, deleter_type>::type pointer; //萃取是否有自定义的指针类型
static_assert(!is_rvalue_reference<deleter_type>::value,
"the specified deleter type cannot be an rvalue reference");
private:
__compressed_pair<pointer, deleter_type> __ptr_;
第一个模板参数_Tp是我们要定义智能指针存储对象的类型,第二个模板参数_Dp则是析构函数的类型。默认的default_delete<_Tp>使用的是直接delete指针的方式。
__compressed_pair则是存储原始指针和析构函数的地方,具体我们稍后再说,先把大体看一遍,
__point是一个类型萃取,源码如下,简单来说就是pointer则是对_Dp进行类型萃取,判断_Dp是否自己定义了自己的point,如_Dp中定义了typedef point xxx,会萃取这个xxx。如果没有就会为_Tp*。
#define _LIBCPP_ALLOCATOR_TRAITS_HAS_XXX(NAME, PROPERTY) \
template <class _Tp, class = void> struct NAME : false_type {
}; \
template <class _Tp> struct NAME<_Tp, typename __void_t<typename _Tp:: PROPERTY >::type> : true_type {
}
// __pointer
_LIBCPP_ALLOCATOR_TRAITS_HAS_XXX(__has_pointer, pointer);
template <class _Tp, class _Alloc,
class _RawAlloc = typename remove_reference<_Alloc>::type,
bool = __has_pointer<_RawAlloc>::value>
struct __pointer {
using type _LIBCPP_NODEBUG_TYPE = typename _RawAlloc::pointer;
};
template <class _Tp, class _Alloc, class _RawAlloc>
struct __pointer<_Tp, _Alloc, _RawAlloc, false> {
using type _LIBCPP_NODEBUG_TYPE = _Tp*;
};
不难看出,__pointer根据第四个模板参数进行选择性的特化,第四个模板参数__has_pointer是利用sfinae进行判断是否存在_Tp::point这样的东西。sfinae在c++应用是非常常见和普遍的。
自定义析构函数的萃取
private:
__compressed_pair<pointer, deleter_type> __ptr_;
struct __nat {
int __for_bool_; };
typedef _LIBCPP_NODEBUG_TYPE __unique_ptr_deleter_sfinae<_Dp> _DeleterSFINAE;
template <bool _Dummy>
using _LValRefType _LIBCPP_NODEBUG_TYPE =
typename __dependent_type<_DeleterSFINAE, _Dummy>::__lval_ref_type;
template <bool _Dummy>
using _GoodRValRefType _LIBCPP_NODEBUG_TYPE =
typename __dependent_type<_DeleterSFINAE, _Dummy>::__good_rval_ref_type;
template <bool _Dummy>
using _BadRValRefType _LIBCPP_NODEBUG_TYPE =
typename __dependent_type<_DeleterSFINAE, _Dummy>::__bad_rval_ref_type;
template <bool _Dummy, class _Deleter = typename __dependent_type<
__identity<deleter_type>, _Dummy>::type>
using _EnableIfDeleterDefaultConstructible _LIBCPP_NODEBUG_TYPE =
typename enable_if<is_default_constructible<_Deleter>::value &&
!is_pointer<_Deleter>::value>::type;
template <class _ArgType>
using _EnableIfDeleterConstructible _LIBCPP_NODEBUG_TYPE =
typename enable_if<is_constructible<deleter_type, _ArgType>::value>::type;
template <class _UPtr, class _Up>
using _EnableIfMoveConvertible _LIBCPP_NODEBUG_TYPE = typename enable_if<
is_convertible<typename _UPtr::pointer, pointer>::value &&
!is_array<_Up>::value
>::type;
template <class _UDel>
using _EnableIfDeleterConvertible _LIBCPP_NODEBUG_TYPE = typename enable_if<
(is_reference<_Dp>::value && is_same<_Dp, _UDel>::value) ||
(!is_reference<_Dp>::value && is_convertible<_UDel, _Dp>::value)
>::type;
template <class _UDel>
using _EnableIfDeleterAssignable = typename enable_if<
is_assignable<_Dp&, _UDel&&>::value
>::type;
public:
template <bool _Dummy = true,
class = _EnableIfDeleterDefaultConstructible<_Dummy> >
_LIBCPP_INLINE_VISIBILITY
_LIBCPP_CONSTEXPR unique_ptr() _NOEXCEPT : __ptr_(pointer(), __default_init_tag()) {
}
template <bool _Dummy = true,
class = _EnableIfDeleterDefaultConstructible<_Dummy> >
_LIBCPP_INLINE_VISIBILITY
_LIBCPP_CONSTEXPR unique_ptr(nullptr_t) _NOEXCEPT : __ptr_(pointer(), __default_init_tag()) {
}
template <bool _Dummy = true,
class = _EnableIfDeleterDefaultConstructible<_Dummy> >
_LIBCPP_INLINE_VISIBILITY
explicit unique_ptr(pointer __p) _NOEXCEPT : __ptr_(__p, __default_init_tag()) {
}
template <bool _Dummy = true,
class = _EnableIfDeleterConstructible<_LValRefType<_Dummy> > >
_LIBCPP_INLINE_VISIBILITY
unique_ptr(pointer __p, _LValRefType<_Dummy> __d) _NOEXCEPT
: __ptr_(__p, __d) {
}
template <bool _Dummy = true,
class = _EnableIfDeleterConstructible<_GoodRValRefType<_Dummy> > >
_LIBCPP_INLINE_VISIBILITY
unique_ptr(pointer __p, _GoodRValRefType<_Dummy> __d) _NOEXCEPT
: __ptr_(__p, _VSTD::move(__d)) {
static_assert(!is_reference<deleter_type>::value,
"rvalue deleter bound to reference");
}
template <bool _Dummy = true,
class = _EnableIfDeleterConstructible<_BadRValRefType<_Dummy> > >
_LIBCPP_INLINE_VISIBILITY
unique_ptr(pointer __p, _BadRValRefType<_Dummy> __d) = delete;
_LIBCPP_INLINE_VISIBILITY
unique_ptr(unique_ptr&& __u) _NOEXCEPT
: __ptr_(__u.release(), _VSTD::forward<deleter_type>(__u.get_deleter())) {
}
template <class _Up, class _Ep,
class = _EnableIfMoveConvertible<unique_ptr<_Up, _Ep>, _Up>,
class = _EnableIfDeleterConvertible<_Ep>
>
_LIBCPP_INLINE_VISIBILITY
unique_ptr(unique_ptr<_Up, _Ep>&& __u) _NOEXCEPT
: __ptr_(__u.release(), _VSTD::forward<_Ep>(__u.get_deleter())) {
}
第一眼看上去是不是感觉很复杂,实际上我也感觉复杂,private中的对析构函数萃取的定义,public中为构造函数,在unique_ptr构造时会利用
我们从第一步_DeleterSFINAE开始分析
typedef _LIBCPP_NODEBUG_TYPE __unique_ptr_deleter_sfinae<_Dp> _DeleterSFINAE;
//具体定义
template <class _Deleter>
struct __unique_ptr_deleter_sfinae {
static_assert(!is_reference<_Deleter>::value, "incorrect specialization");
typedef const _Deleter& __lval_ref_type;
typedef _Deleter&& __good_rval_ref_type;
typedef true_type __enable_rval_overload;
};
template <class _Deleter>
struct __unique_ptr_deleter_sfinae<_Deleter const&> {
typedef const _Deleter& __lval_ref_type;
typedef const _Deleter&& __bad_rval_ref_type;
typedef false_type __enable_rval_overload;
};
template <class _Deleter>
struct __unique_ptr_deleter_sfinae<_Deleter&> {
typedef _Deleter& __lval_ref_type;
typedef _Deleter&& __bad_rval_ref_type;
typedef false_type __enable_rval_overload;
};
_DeleterSFINAE实际上是__unique_ptr_deleter_sfinae<_Dp>的别称,__unique_ptr_deleter_sfinae<_Dp>会根据模板参数_Dp的类型进行特化,如果是值的话则有__good_rval_ref_type和__enable_rval_overload为true_type。如果为引用的话则有__bad_rval_ref_type和__enable_rval_overload为false_type。
这里__good_rval_ref_type和__bad_rval_ref_type解为后续的萃取使用。
第二步是接着看是谁用了这个_DeleterSFINAE,我们可以发现是_GoodRValRefType和_BadRValRefType
template <bool _Dummy>
using _LValRefType _LIBCPP_NODEBUG_TYPE =
typename __dependent_type<_DeleterSFINAE, _Dummy>::__lval_ref_type;
template <bool _Dummy>
using _GoodRValRefType _LIBCPP_NODEBUG_TYPE =
typename __dependent_type<_DeleterSFINAE, _Dummy>::__good_rval_ref_type;
template <bool _Dummy>
using _BadRValRefType _LIBCPP_NODEBUG_TYPE =
typename __dependent_type<_DeleterSFINAE, _Dummy>::__bad_rval_ref_type;
//__dependent_type
template <class _Tp, bool>
struct _LIBCPP_TEMPLATE_VIS __dependent_type : public _Tp {
};
这里_LValRefType,_GoodRValRefType和_BadRValRefType的作用是把_DeleterSFINAE中类型取出,__dependent_type则是用来推迟决议的,;利用了第二个模板参数
我引用下stackoverflow中的回答,stackoverflow上这个问题是_Dummy是有什么用的。
回答:
It is the dummy bool that makes the type dependent, this is the whole point of __dependent_type, otherwise you can just use the type itself.
Take this code as example:
template <bool _Dummy>
using _GoodRValRefType =
typename __dependent_type<_DeleterSFINAE, _Dummy>::__good_rval_ref_type;
Without the dummy to make it a dependent type, when the class template gets instantiated, _DeleterSFINAE::__good_rval_ref_type might cause a hard error, because not all of the _DeleterSFINAE has a __good_rval_ref_type member.
The dependent type delays evaluation, so that you can use _GoodRValRefType in a SFINAE context later.
大致意思就让其变成type dependent,延迟决定。
然后接着就是一些利用enable_if判断特性了定义了,这些就比较简单了,
using _EnableIfDeleterDefaultConstructible _LIBCPP_NODEBUG_TYPE =
typename enable_if<is_default_constructible<_Deleter>::value &&
!is_pointer<_Deleter>::value>::type;
template <class _ArgType>
using _EnableIfDeleterConstructible _LIBCPP_NODEBUG_TYPE =
typename enable_if<is_constructible<deleter_type, _ArgType>::value>::type;
template <class _UPtr, class _Up>
using _EnableIfMoveConvertible _LIBCPP_NODEBUG_TYPE = typename enable_if<
is_convertible<typename _UPtr::pointer, pointer>::value &&
!is_array<_Up>::value
>::type;
template <class _UDel>
using _EnableIfDeleterConvertible _LIBCPP_NODEBUG_TYPE = typename enable_if<
(is_reference<_Dp>::value && is_same<_Dp, _UDel>::value) ||
(!is_reference<_Dp>::value && is_convertible<_UDel, _Dp>::value)
>::type;
template <class _UDel>
using _EnableIfDeleterAssignable = typename enable_if<
is_assignable<_Dp&, _UDel&&>::value
>::type;
最后一步就是结合各种构造函数去对应上面的萃取了,感觉看的时候还行,但是一旦要把这些东西理出来还是有点顶,至于什么的这么复杂,我个人认为为了推迟_DeleterSFINAE的绑定查找,也就是前面stackoverflow的回答。
__compressed_pair<pointer, deleter_type> _ptr;
存储原始指针和自定义析构函数的结果,简单的来说是一个优化过的pair,是空基类优化,当没有自定义的析构函数时,不会浪费空间。