底层一般是一个compressed_pair,运用c++中的空基类优化。
boost中有实现compressed_pair.hpp
微软stl中的实现compressed_pair
便于理解做简化
template<typename T1,typename T2, bool = std::is_empty_v<T1>> struct compressed_pair : T1 { T2 val2; } template<typename T1,typename T2> struct compressed_pair<T1,T2,false> { T1 val1; T2 val2; };
简单理解就是如果T1类型是空类的化,就继承它,利用空基类优化。否则就是个普通的pair。
这意味着如果你的删除器是无状态的话,unique_ptr尺寸就是一个指针大小。如果携带状态,就会导致尺寸变大。所以并不是定义删除器就会增大尺寸。
std::unique_ptr
需要在类内保存管理的对象指针和删除器对象,在 MSVC 的实现中,底层保存一个 _Compressed_pair
,它能在删除器为空时应用空基类优化,以减小对象大小。这就是题主在不提供删除器时(使用默认的删除器 std::default_delete
) 大小仅仅包含一个指针的大小的原因。
这里以我手头的 MSVC 的标准库实现为例:
_Compressed_pair
的实现如下:
template <class _Ty1, class _Ty2, bool = is_empty_v<_Ty1> && !is_final_v<_Ty1>> class _Compressed_pair final : private _Ty1 { // store a pair of values, deriving from empty first public: _Ty2 _Myval2; ... }; template <class _Ty1, class _Ty2> class _Compressed_pair<_Ty1, _Ty2, false> final { // store a pair of values, not deriving from first public: _Ty1 _Myval1; _Ty2 _Myval2; ... };
_Compressed_pair
的第三个模板参数的默认值用于确定该使用主模板还是其特化。通过判断第一个类型 _Ty1
是否为空(is_empty_v<_Ty1>
)和是否可以继承(不为 final
,即 !is_final_v<_Ty1>
),选用两个特化。
其中,若 _Ty1
为空且可继承,则继承自 _Ty1
,以实施空基类优化。我想注释中已经说的比较明确了。
为了构造 _Compressed_pair
对象,还定义了两个 tag:
struct _Zero_then_variadic_args_t { explicit _Zero_then_variadic_args_t() = default; }; // tag type for value-initializing first, constructing second from remaining args struct _One_then_variadic_args_t { explicit _One_then_variadic_args_t() = default; }; // tag type for constructing first from one arg, constructing second from remaining args
第一个 tag 表示将给定的实参全部用来构造 _Ty2
对象,而 _Ty1
对象被值初始化。因此,使用了该 tag 的构造函数是这样实现的:
template <class... _Other2> constexpr explicit _Compressed_pair(_Zero_then_variadic_args_t, _Other2&&... _Val2) noexcept( conjunction_v<is_nothrow_default_constructible<_Ty1>, is_nothrow_constructible<_Ty2, _Other2...>>) : _Ty1(), _Myval2(_STD forward<_Other2>(_Val2)...) {}
此处仅仅展示主模板中该构造函数的定义,而特化中也有类似的定义,只不过初始化的对象不同而已。
第二个 tag 表示使用第一个实参构造 _Ty1
对象,使用剩余实参构造 _Ty2
对象,具体应用为:
template <class _Other1, class... _Other2> constexpr _Compressed_pair(_One_then_variadic_args_t, _Other1&& _Val1, _Other2&&... _Val2) noexcept( conjunction_v<is_nothrow_constructible<_Ty1, _Other1>, is_nothrow_constructible<_Ty2, _Other2...>>) : _Ty1(_STD forward<_Other1>(_Val1)), _Myval2(_STD forward<_Other2>(_Val2)...) {}
std::unique_ptr
就是使用该结构实现的:
template <class _Ty, class _Dx /* = default_delete<_Ty> */> class unique_ptr { // non-copyable pointer to an object ... private: _Compressed_pair<_Dx, pointer> _Mypair; };
_Ty1
就是删除器类型,_Ty2
就是 pointer
类型,而 pointer
是 unique_ptr
的一个公有类型成员,就是要管理的指针类型。
至于 unique_ptr
不能复制,是因为其复制构造函数和复制赋值运算符都被弃置了:
unique_ptr(const unique_ptr&) = delete; unique_ptr& operator=(const unique_ptr&) = delete;
了解清楚 _Compressed_pair
的两个类型的对应关系后,我想其它成员函数也是比较好理解的。unique_ptr
代码也不长,可以试着读一读。