代码之家  ›  专栏  ›  技术社区  ›  choxsword

如果unique\u ptr需要存储deleter,它怎么可能没有开销?

  •  28
  • choxsword  · 技术社区  · 7 年前

    首先看一下《C++Primer》是怎么说的 unique_ptr shared_ptr :
    16.1.6美元。效率和灵活性

    我们可以肯定 共享\u ptr 不将删除者作为直接成员, 因为直到运行时才知道删除器的类型。

    因为删除器的类型是 唯一\u ptr ,则在编译时已知deleter成员的类型。 删除器可以直接存储在每个 唯一\u ptr 对象

    看来 共享\u ptr 没有deleter的直接成员,但 唯一\u ptr 做然而 the top-voted answer of another question 表示:

    如果提供deleter as template参数(如 唯一\u ptr )这是类型的一部分 不需要在该类型的对象中存储任何其他内容 。如果将deleter作为构造函数的参数传递(如 共享\u ptr ) 您需要将其存储在对象中 。这是额外灵活性的成本,因为您可以对相同类型的对象使用不同的删除器。

    引用的两段内容完全矛盾,这让我感到困惑。更重要的是, many people says unique_ptr is zero overhead 因为它不需要将删除器存储为成员。然而,正如我们所知, 唯一\u ptr 具有构造函数 unique_ptr<obj,del> p(new obj,fcn) ,这意味着我们可以向它传递一个deleter,所以 唯一\u ptr 似乎已将deleter存储为成员。真是一团糟!

    4 回复  |  直到 5 年前
        1
  •  35
  •   Angew is no longer proud of SO    7 年前

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

    原因很简单:如果deleter是空的(因而是无状态的)类型(例如 std::default_delete 实例化)。

        2
  •  12
  •   MSalters    7 年前

    让你困惑的关键短语是“删除者” 可以 但存储类型为“”的删除器没有意义 std::default_delete 。如果需要,可以创建一个 std::default_delete{}

    通常,无状态删除程序不需要存储,因为您可以根据需要创建它们。

        3
  •  11
  •   Passer By    7 年前

    Angew's answer 非常彻底地解释了发生的事情。

    对于那些好奇的人来说

    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;
    
        // ...
    };
    

    专门用于空删除程序并利用 empty base optimization

        4
  •  1
  •   NoSenseEtAl    7 年前

    简介:

    唯一\u ptr 可以 引入一些小的开销,但这并不是因为deleter,而是因为当您离开它时,值必须设置为null,如果您使用的是原始指针,那么旧指针可能会处于容易出现错误但合法的状态,它仍然指向它之前指向的位置。显然,智能优化器可以进行优化,但不能保证。

    返回删除程序:

    其他答案是正确的,但要详细说明。因此,这里是简化版,没有提及EBO或其他复杂术语。

    如果deleter为空(没有状态),则不需要将其保存在unique\u ptr中。如果你需要它,你可以在需要的时候构建它。您只需要知道deleter类型(这是unique\u ptr的模板参数之一)。

    例如,考虑下面的代码,然后还演示了按需创建无状态对象的简单过程。

    #include <iostream>
    #include <string>
    #include <string_view>
    
    template<typename Person>
    struct Greeter{
        void greet(){
            static_assert(std::is_empty_v<Person>, "Person must be stateless");
            Person p; // Stateless Person instance constructed on demand
            std::cout << "Hello " << p() << std::endl;
        }
        // ... and not kept as a member.
    };
    
    struct Bjarne{
        std::string_view operator()(){
            return "Bjarne";
        }
    };
    
    int main() {
        Greeter<Bjarne> hello_bjarne;
        hello_bjarne.greet();
    }