如果需要存储删除器,unique_ptr怎么没有开销?

big*_*iao 27 c++ smart-pointers unique-ptr c++-standard-library c++11

先来看看什么C++入门谈到unique_ptrshared_ptr:
$ 16.1.6.效率和灵活性

我们可以确定shared_ptr不将删除器作为直接成员,因为删除器的类型直到运行时才知道.

因为删除器的类型是a unique_ptr类型的一部分,所以删除器成员的类型在编译时是已知的.删除器可以直接存储在每个unique_ptr对象中.

所以似乎shared_ptr没有直接的删除成员,但unique_ptr确实如此.然而,另一个问题的最高投票回答说:

如果将deleter作为模板参数提供(如unique_ptr),则它是类型的一部分,您不需要在此类型的对象中存储任何其他内容.如果将deleteter作为构造函数的参数传递(如shared_ptr),则需要将其存储在对象中.这是额外灵活性的代价,因为您可以为相同类型的对象使用不同的删除器.

引用的两段完全相互矛盾,让我感到困惑.更重要的是,许多人说unique_ptr是零开销,因为它不需要将删除器存储为成员.但是,正如我们所知,unique_ptr有一个构造函数unique_ptr<obj,del> p(new obj,fcn),这意味着我们可以将删除函数传递给它,因此unique_ptr似乎已将删除函数存储为成员.真是一团糟!

Rei*_*ica 32

std::unique_ptr<T>很可能是零开销(任何理智的标准库实现).std::unique_ptr<T, D>,对于任意的D,通常不是零开销.

原因很简单:如果删除器是空的(因此是无状态的)类型(例如std::default_delete实例化),则可以使用空基优化来消除删除器的存储.

  • 切向但值得早期传播意识:C++ 20正在通过新属性`[[no_unique_address]]来允许空**成员**优化. (12认同)
  • 我没有看到冲突.第一个说`unique_ptr`*可以*存储删除器.第二个说有时它确实有时它有时它没有. (4认同)
  • @bigxiao - 这就是它的美丽.删除器的类型在模板参数中指定.因此,如果用户传递删除器,则智能指针只需要从中复制初始化它的基类.如果基类没有内存,那么它就是零成本抽象.如果确实如此,它确实没有任何皮肤. (4认同)
  • @bigxiao对象*的大小可能小于它的基数和数据成员的大小之和.`sizeof(std :: default_delete <int>)`非零 (3认同)
  • @bigxiao - 对EBO感觉相当容易,请参阅这个小型测试程序http://coliru.stacked-crooked.com/a/4ac999ea6a6a3885 (2认同)

MSa*_*ers 12

似乎让你困惑的关键词是"删除器可以直接存储".但是存储类型的删除器是没有意义的std::default_delete.如果你需要一个,你可以创建一个std::default_delete{}.

通常,无需存储无状态删除程序,因为您可以按需创建它们.

  • 虽然是真的,但我希望大多数 `std::unique_ptr` 实现_do_ 存储删除器,它们只是使用 0 字节。 (3认同)

Pas*_* By 11

Angew的回答非常彻底地解释了发生了什么.

对于那些好奇的事情,看起来如何

template<typename T, typename D, bool Empty = std::is_empty_v<D>>
class unique_ptr
{
    T* ptr;
    D d;

    // ... 
};

template<typename T, typename D>
class unique_ptr<T, D, true> : D
{
    T* ptr;

    // ...
};
Run Code Online (Sandbox Code Playgroud)

其中专门用于空删除并利用空基优化.