STL源码阅读小记(四)——Unique_ptr

前言

继续看stl中常用东西的源码,打算把智能指针的坑给填了,这次先写点unique_prt,下次写shared_ptr。unique_prt的代码总体而言比较简单,唯一稍微复杂的地方就是对自定义析构函数的萃取。

STL版本

本文所使用的stl版本为libc++ 13.0,属于LLVM项目。

LLVM项目Github

如果遇到不熟悉的宏定义可以参考文档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,是空基类优化,当没有自定义的析构函数时,不会浪费空间。

猜你喜欢

转载自blog.csdn.net/ninesnow_c/article/details/123063863
今日推荐