简体   繁体   English

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

[英]How can unique_ptr have no overhead if it needs to store the deleter?

First take a look at what C++ Primer said about unique_ptr and shared_ptr : 首先看一下C ++ Primer关于unique_ptrshared_ptr
$16.1.6. $ 16.1.6。 Efficiency and Flexibility 效率和灵活性

We can be certain that shared_ptr does not hold the deleter as a direct member, because the type of the deleter isn't known until run time. 我们可以确定shared_ptr不将删除器保存为直接成员,因为直到运行时才知道删除器的类型。

Because the type of the deleter is part of the type of a unique_ptr , the type of the deleter member is known at compile time. 因为删除器的类型是unique_ptr类型的一部分,所以删除器成员的类型在编译时是已知的。 The deleter can be stored directly in each unique_ptr object. 删除器可以直接存储在每个unique_ptr对象中。

So it seems like that the shared_ptr does not have a direct member of deleter, but unique_ptr does. 所以似乎shared_ptr没有删除器的直接成员,但unique_ptr确实如此。 However, the top-voted answer of another question says: 然而, 另一个问题的最高投票回答说:

If you provide the deleter as template argument (as in unique_ptr ) it is part of the type and you don't need to store anything additional in the objects of this type . 如果您将deleter作为模板参数提供(如在unique_ptr ),则它是该类型的一部分,您不需要在此类型的对象中存储任何其他内容 If deleter is passed as constructor's argument (as in shared_ptr ) you need to store it in the object . 如果将deleteter作为构造函数的参数传递(如在shared_ptr,则需要将其存储在对象中 This is the cost of additional flexibility, since you can use different deleters for the objects of the same type. 这是额外灵活性的代价,因为您可以为相同类型的对象使用不同的删除器。

The two quoted paragraph are totally conflicting, which makes me confused. 引用的两段完全相互矛盾,让我感到困惑。 What's more, many people says unique_ptr is zero overhead because it doesn't need to store the deleter as member. 更重要的是, 许多人说unique_ptr是零开销,因为它不需要将删除器存储为成员。 However, as we know, unique_ptr has a constructor of unique_ptr<obj,del> p(new obj,fcn) , which means that we can pass a deleter to it, so unique_ptr seems to have stored deleter as a member. 但是,正如我们所知, unique_ptr有一个unique_ptr<obj,del> p(new obj,fcn)的构造函数,这意味着我们可以向它传递一个删除器,因此unique_ptr似乎已将删除器存储为成员。 What a mess! 真是一团糟!

std::unique_ptr<T> is quite likely to be zero-overhead (with any sane standard-library implementation). std::unique_ptr<T>很可能是零开销(任何理智的标准库实现)。 std::unique_ptr<T, D> , for an arbitrary D , is not in general zero-overhead. 对于任意Dstd::unique_ptr<T, D>通常不是零开销。

The reason is simple: Empty-Base Optimisation can be used to eliminate storage of the deleter in case it's an empty (and thus stateless) type (such as std::default_delete instantiations). 原因很简单:如果删除器是空的(因此是无状态的)类型(例如std::default_delete instantiations),则可以使用Empty-Base Optimization来消除删除器的存储。

The key phrase which seems to confuse you is "The deleter can be stored directly". 似乎让你困惑的关键词是“删除器可以直接存储”。 But there's no point in storing a deleter of type std::default_delete . 但是存储类型为std::default_delete的删除器没有意义。 If you need one, you can just create one as std::default_delete{} . 如果你需要一个,你可以创建一个std::default_delete{}

In general, stateless deleters do not need to be stored, as you can create them on demand. 通常,无需存储无状态删除程序,因为您可以按需创建它们。

Angew's answer explained pretty thoroughly what's going on. Angew的回答非常彻底地解释了发生了什么。

For those curious how things could look under the covers 对于那些好奇的事情,看起来如何

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;

    // ...
};

Which specializes for empty deleters and take advantage of empty base optimization . 其中专门用于空删除并利用空基优化

Brief intro: 简介:

unique_ptr can introduce some small overhead, but not because of the deleter, but because when you move from it value must be set to null where if you were using raw pointers you could leave the old pointer in bug prone but legitimate state where it still points to where it pointed before. unique_ptr 可以引入一些小的开销,但不是因为删除,而是因为当你从它移动时,value必须设置为null,如果你使用原始指针,你可以将旧指针留在容易出错但合法的状态,它仍然指向它指向的地方。 Obviously smart optimizer can optimize, but it is not guaranteed. 显然智能优化器可以优化,但不能保证。

Back to the deleter: 回到删除者:

Other answers are correct, but elaborate. 其他答案是正确的,但详细说明。 So here is the simplified version witout mention of EBO or other complicated terms. 所以这里是简化版本,没有提到EBO或其他复杂的术语。

If deleter is empty(has no state) you do not need to keep it inside the unique_ptr. 如果deleter为空(没有状态),则不需要将其保留在unique_ptr中。 If you need it you can just construct it when you need it. 如果您需要它,您可以在需要时构建它。 All you need to know is the deleter type(and that is one of the template arguments for unique_ptr). 您需要知道的是删除类型(这是unique_ptr的模板参数之一)。

For exaple consider following code, than also demonstrates simple creation on demand of a stateless object. 对于exaple,请考虑以下代码,而不是演示无状态对象的简单创建。

#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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM