#include <memory>
#include <cstdio>
int main(){
auto x1 = [](int *p){ delete(p); };
auto ptr = std::unique_ptr<int, decltype(x1)>(new int{1},x1);
printf("%zu\n", sizeof(ptr)); // 8, unexpected
}
带有 lambda 的类型的大小为 8
跟进这个问题:如果使用 lambda,std::unique_ptr 如何没有大小开销
我的结论是,EBO(空基类优化)通过继承空基类来避免增加对象大小
为此,让我们转到 Github 存储库以获取 unique_ptr 的 Microsoft STL 实现:
然后如果我们转到第 1229 行:
https://github.com/microsoft/STL/blob/master/stl/inc/memory#L1229
您可以看到以下帮助程序类型:
_Compressed_pair<_Dx, pointer> _Mypair;
该实现将指针和删除器存储在压缩对内。
在整个类代码中,您可以注意到 unique_ptr 使用 _Mypair 对象来引用数据和删除器。例如在析构函数中:
~unique_ptr() noexcept {
if (_Mypair._Myval2) {
_Mypair._Get_first()(_Mypair._Myval2); // call deleter
}
}
我们看一下代码:
这次我们要进入xmemory头了:
https://github.com/microsoft/STL/blob/master/stl/inc/xmemory#L1487
我们有两个模板专业化:
第一个:
type here// store a pair of values, deriving from empty first
template <class _Ty1, class _Ty2, bool = is_empty_v<_Ty1> &&
!is_final_v<_Ty1>>
class _Compressed_pair final : private _Ty1 {
public:
_Ty2 _Myval2;
// ... the rest of impl
第二个:
// store a pair of values, not deriving from first
template <class _Ty1, class _Ty2>
class _Compressed_pair<_Ty1, _Ty2, false> final {
public:
_Ty1 _Myval1;
_Ty2 _Myval2;
// ... the rest of impl
压缩对非常简单,因为它只考虑第一个类型是否为空。我认为
std::unique_ptr<int, decltype(x1)>)
使用第一个,但在 C++20 之前,闭包类型没有(自 C++14 起)默认构造函数。,所以第一个中的_Compressed_pair
派生了没有默认构造函数的基类,它如何创建实例_Compressed_pair<_Dx, pointer> _Mypair;
?
如果删除了基类的构造函数,那么子类也无法构造对象。那么在C++20之前unique_ptr是如何在闭包上应用EBO的呢?
如果删除了基类的构造函数,那么子类也无法构造对象。
仅 lambda 的默认构造函数被删除,但它仍然能够被复制/移动构造。
static_assert(!std::is_default_constructible_v<decltype(x1)>); // different since C++20
static_assert(std::is_move_constructible_v<decltype(x1)>);
static_assert(std::is_copy_constructible_v<decltype(x1)>);
_Compressed_pair
提供了一个构造函数,允许对Ty_1
进行复制/移动初始化。
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 时,您提供
x1
作为参数。
auto ptr = std::unique_ptr<int, decltype(x1)>(new int{1},x1);
这就是
_Mypair
的构造方式。