std::unique_ptr在移动时如何将另一个指针设置为null?

huangapple go评论76阅读模式
英文:

How does std::unique_ptr set the other pointer to null when moving?

问题

以下是翻译好的内容:

当发生移动操作时,通常一个类将other的指针设置为null,这也适用于std::unique_ptr。赋值运算符被模板化,以便您可以将unique_ptr移动到不同类型(即派生类)。

我的问题是:它如何访问另一个类中的ptr,它是一个private成员,鉴于它是一个实例化的模板类,因此不同,并且不能访问private甚至protected成员?

template <typename T>
class MyPointer
{
public:
    template <typename U>
    void operator=(const MyPointer<U>& other) = delete;

    template <typename U>
    void operator=(MyPointer<U>&& other)
    {
        // 不会编译通过,这是与参数不同的类
        // 无法访问私有成员
        other.pointer = nullptr;
        
    }
    char* get() const { return pointer; }

private:
    char* pointer;
};

int main()
{
    struct B {};
    struct D : B {};

    std::unique_ptr<B> base;
    std::unique_ptr<D> derived;

    // std::unique_ptr在移动时以某种方式将另一个指针设置为null
    base = std::move(derived);

    MyPointer<B> my_pointer_b;
    MyPointer<D> my_pointer_d;

    // 无法访问私有成员
    my_pointer_b = std::move(my_pointer_d); 
}
英文:

When a move happens, usually a class sets the other's pointer to null, and this is the case with std::unique_ptr. The assignment operator is templated so that you can move unique_ptrs to different types (i.e derived classes).

My question is: How can it access the ptr in the other class, a private member, given that it's an instantiated templated class, and therefore different, and can't access private or even protected members?

template &lt;typename T&gt;
class MyPointer
{
public:
    template &lt;typename U&gt;
    void operator=(const MyPointer&lt;U&gt;&amp; other) = delete;

    template &lt;typename U&gt;
    void operator=(MyPointer&lt;U&gt;&amp;&amp; other)
    {
        // WON&#39;T COMPILE, THIS IS A DIFFERENT CLASS FROM THE ARGUMENT
        // CANNOT ACCESS PRIVATE MEMBER 
        other.pointer = nullptr;
        
    }
    char* get() const { return pointer; }

private:
    char* pointer;
};

int main()
{
    struct B {};
    struct D : B {};

    std::unique_ptr&lt;B&gt; base;
    std::unique_ptr&lt;D&gt; derived;

    // std::unique_ptr somehow sets the other pointer to null when moving
    base = std::move(derived);

    MyPointer&lt;B&gt; my_pointer_b;
    MyPointer&lt;D&gt; my_pointer_d;

    // CANNOT ACCESS PRIVATE MEMBER
    my_pointer_b = std::move(my_pointer_d); 
}

答案1

得分: 1

Here is the translated content:

我的问题是,如何访问另一个类中的 ptr,它是一个private成员,鉴于它是一个实例化的模板类,因此是不同的,无法访问private甚至protected成员?

正如其他人提到的,您漏掉了一个明显的细节。来自 cppreference.com移动赋值 std::unique_ptr::operator=解释了如何处理 std::unique_ptr&lt;T, Deleter&gt;(重点在于):

unique_ptr&amp; operator=(unique_ptr&amp;&amp; r) noexcept;// (1) (constexpr since C++23)

[...]

1) 移动赋值运算符。reset(r.release())的方式将所有权从 r 转移到 *this,然后将std::forward&lt;Deleter&gt;(r.get_deleter())的值赋给get_deleter()

[...]

您还可以重写您的 MyPointer 如下:

template &lt;typename T, typename Deleter = std::default_delete&lt;T&gt;&gt;
class MyPointer
{
public:
    // ... 其他成员

    template &lt;typename U, typename OtherDeleter&gt;
    MyPointer&amp; operator=(MyPointer&lt;U, OtherDeleter&gt;&amp;&amp; other) noexcept
    {
        // 就像 std::unique_ptr 一样!
        reset(other.release());
        deleter = std::forward&lt;OtherDeleter&gt;(other.get_deleter());
        return *this;
    }

    T* release() noexcept
    {
        T* releasedPtr = pointer;
        pointer = nullptr;
        return releasedPtr;
    }

    void reset(T* newPtr = nullptr) noexcept
    {
        if (pointer != newPtr) 
        {
            deleter(pointer);
            pointer = newPtr;
        }
    }

    template&lt;typename Self&gt; // 需要 C++23 支持 !!
    decltype(auto) get_deleter(this Self&amp;&amp; self) noexcept
    {
        return std::forward&lt;Self&gt;(self).deleter;
    }

private:
    T* pointer{ nullptr}; // 代替 char*(也许?)
    Deleter deleter;
};

或者,您可以将 MyPointer 类声明为 otherfriend

template &lt;typename T&gt; class MyPointer 
{
public:
    // .... 其他成员!
private:
    T* pointer;

    // 将 MyPointer&lt;T&gt; 声明为 MyPointer&lt;U&gt; 的 friend 
    template &lt;typename U&gt;
    friend class MyPointer;  
};

这不太推荐,因为它破坏了封装的思想,可能被视为对 friend 声明的误用。

英文:

> My question is how can it access the ptr in the other class, a private member, given that it's an instantiated templated class, and therefore different, and can't access private or even protected members?

As others mentioned, you are missing the obvious detail. From the cppreference.com the move assignment std::unique_ptr::operator= explains, how this has been handled for the std::unique_ptr&lt;T, Deleter&gt;(emphasis mine):

> unique_ptr&amp; operator=( unique_ptr&amp;&amp; r ) noexcept;// (1) (constexpr since C++23)
>
> [...]
>
>1) Move assignment operator. Transfers ownership from r to *this as if by calling reset(r.release()) followed by an assignment of get_deleter() from std::forward&lt;Deleter&gt;(r.get_deleter()).
>
>[...]

You could also rework your MyPointer the same:

template &lt;typename T, typename Deleter = std::default_delete&lt;T&gt;&gt;
class MyPointer
{
public:
    // ... other members

    template &lt;typename U, typename OtherDeleter&gt;
    MyPointer&amp; operator=(MyPointer&lt;U, OtherDeleter&gt;&amp;&amp; other) noexcept
    {
        // Just like the std::unique_ptr does!
        reset(other.release());
        deleter = std::forward&lt;OtherDeleter&gt;(other.get_deleter());
        return *this;
    }

    T* release() noexcept
    {
        T* releasedPtr = pointer;
        pointer = nullptr;
        return releasedPtr;
    }

    void reset(T* newPtr = nullptr) noexcept
    {
        if (pointer != newPtr) 
        {
            deleter(pointer);
            pointer = newPtr;
        }
    }

    template&lt;typename Self&gt; // requires C++23 support !!
    decltype(auto) get_deleter(this Self&amp;&amp; self) noexcept
    {
        return std::forward&lt;Self&gt;(self).deleter;
    }

private:
    T* pointer{ nullptr}; // instead of char* (maybe ?)
    Deleter deleter;
};

<sup>†</sup> Alternatively, you could make the MyPointer class be friend of the other.

template &lt;typename T&gt; class MyPointer 
{
public:
    // .... other members!
private:
    T* pointer;

    // Declare MyPointer&lt;T&gt; as a friend of MyPointer&lt;U&gt;
    template &lt;typename U&gt;
    friend class MyPointer;  
};

<sup>†</sup> This is less recommended, due to the fact, it spoils the idea of encapsulation, and it can be considered as a misuse of friend declaration.

huangapple
  • 本文由 发表于 2023年7月6日 21:11:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/76629211.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定