c – unique_ptr的赋值操作符复制引用存储的删除器.是功能还是错误?

前端之家收集整理的这篇文章主要介绍了c – unique_ptr的赋值操作符复制引用存储的删除器.是功能还是错误?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
当您有一个unique_ptr与引用存储的自定义删除程序成像时:
struct CountingDeleter
{
    void operator()(std::string *p) {
        ++cntr_;
        delete p;
    }

    unsigned long cntr_ = 0;
};

int main()
{
    CountingDeleter d1{},d2{};

    {
        std::unique_ptr<std::string,CountingDeleter&>
            p1(new std::string{"first"},d1),p2(new std::string{"second"},d2);

        p1 = std::move(p2); // does d1 = d2 under cover
    }

    std::cout << "d1 " << d1.cntr_ << "\n"; // output: d1 1
    std::cout << "d2 " << d2.cntr_ << "\n"; // output: d2 0
}

对我来说,令人吃惊的是,上述代码中的赋值对d2进行了复制.我仔细检查一下,发现这个行为是在[unique.ptr.single.asgn]标准中描述的:

(1) – Requires: If D is not a reference type,D shall satisfy the requirements of MoveAssignable and assignment of the deleter from an rvalue of type D shall not throw an exception.
Otherwise,D is a reference type; remove_reference_t<D> shall satisfy the CopyAssignable requirements and assignment of the deleter from an lvalue of type D shall not throw an exception.

(2) – Effects: Transfers ownership from u to *this as if by calling reset(u.release()) followed by get_deleter() = std::forward<D>(u.get_deleter()).

为了获得我预期的行为(一个浅拷贝的删除引用),我不得不将删除引用包装到std :: reference_wrapper中:

std::unique_ptr<std::string,std::reference_wrapper<CountingDeleter>>
    p1(new std::string{"first"},d2);

p1 = std::move(p2); // p1 now stores reference to d2 => no side effects!

对于我来说,目前在独特的ptr中处理一个删除引用是反直觉甚至容易出错的:

>当您使用引用而不是按值存储删除器时,这主要是因为您希望共享删除程序具有一些重要的独特状态.所以你不要指望共享删除器被覆盖,并且在一个唯一的ptr赋值之后它的状态丢失.
>预期unique_ptr的分配是非常重要的,特别是如果删除者是参考.但是,除此之外,您可以复制删除器可能(意外地)昂贵的东西.
>分配后,指针变得绑定到原始的删除者的副本,而不是原始的删除器本身.如果去除者的身份重要,这可能会导致一些意外的副作用.
>此外,当前的行为阻止对删除器使用const引用,因为您不能复制到const对象中.

IMO最好禁止参考类型的删除者,只接受可移动的值类型.

所以我的问题是如下(它看起来像是两个问题,对不起):

>为什么标准的unique_ptr行为是这样的?
>有没有人有一个很好的例子,在unique_ptr而不是非引用类型(即值类型)中引用引用类型删除是有用的?

解决方法

这是一个功能.

如果你有状态的删除者,可能状态是重要的,并且与将被用于删除的指针相关联.这意味着当指针的所有权转移时应该转移删除状态.

但是,如果通过引用存储删除器,则意味着您关心删除者的身份,而不仅仅是其值(即它的状态),并且更新unique_ptr不应将引用重新绑定到另一个对象.

所以如果你不想要这个,你为什么甚至通过参考存储一个删除器?

参考文献的浅层副本甚至意味着什么? C中没有这样的东西.如果您不希望引用语义,请不要使用引用.

如果你真的想这样做,那么解决方案很简单:为你的删除器定义分配,不要改变计数器:

CountingDeleter&
operator=(const CountingDeleter&) noexcept
{ return *this; }

或者,因为你真正关心的是计数器,而不是去除器,将计数器保留在除去器外,不要使用参考去除器:

struct CountingDeleter
{
    void operator()(std::string *p) {
        ++*cntr_;
        delete p;
    }

    unsigned long* cntr_;
};

unsigned long c1 = 0,c2 = 0;
CountingDeleter d1{&c1},d2{&c2};

{
    std::unique_ptr<std::string,CountingDeleter>
        p1(new std::string{"first"},d2);
原文链接:https://www.f2er.com/c/114364.html

猜你在找的C&C++相关文章