简体   繁体   中英

Returning unique_ptr private member data without transferring ownership

Context

The error below appears to be telling me that I can not return my unique_ptr called m_head from this get function. I just want to return my unique_ptr m_head without transferring ownership.

I've been avoiding raw pointers completely since introduced to smart pointers since raw pointers are not exception safe, have the memory management overhead and other issues I've been aware of. Maybe there are cases like this where I should use them briefly contained in a small scope?

In this I think instead of my current approach I need to transfer ownership. I should instead get the object managed by the unique_ptr , create a new shared_ptr to manage the object, then return the shared_ptr , but need some confirmation. I think this may be the case because the std::unique_ptr docs say:

unique_ptr objects own their pointer uniquely: no other facility shall take care of deleting the object, and thus no other managed pointer should point to its managed object, since as soon as they have to, unique_ptr objects delete their managed object without taking into account whether other pointers still point to the same object or not, and thus leaving any other pointers that point there as pointing to an invalid location .

Error

 `function "std::unique_ptr<_Ty, _Dx>::unique_ptr(const std::unique_ptr<_Ty, _Dx> &) 
 [with _Ty=mrobsmart::LinkedList::Node, _Dx=std::default_delete<mrobsmart::LinkedList::Node>]" 

 (declared at line 2337 of "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.15.26726\include\memory")

 cannot be referenced  -- it is a deleted function

Code

#include <memory>

class LinkedList
{
    private:
        std::unique_ptr<Node> m_head;

    public:
        LinkedList(int data) {
            m_head = std::make_unique<Node>(data);
        }

        const std::unique_ptr<Node> get_head() { return m_head; }
};

I just want to return my unique_ptr m_head without transferring ownership.

That's not possible. unique_ptr is designed around the behavior that every move transfers its ownership.

Note, I've been avoiding raw pointers completely since introduced to smart pointers since raw pointers are not exception safe, have the memory management overhead and other issues I've been aware of, but maybe there are cases like this where I should use them briefly contained in a small scope?

Raw pointers are not evil. Using them as pure references/indirections is a perfectly valid use case -- there is no ownership, memory management or exception safety involved.

Of course, it's also possible to return a C++ reference. Whether you choose a pointer or a reference can depend on whether the value can be null, but ultimately is also a question of code style.

So, either of those (important: const -qualify the function):

    const Node* get_head() const { return m_head.get(); }
    const Node& get_head() const { return *m_head; }

The "problem behind the problem" may be that you're trying to use a unique_ptr in a case where you shouldn't. I agree that one should avoid raw pointers, but there options other than these two as well.

Two important properties of std::unique_ptr are:

  1. The ability to represent a value which is not set (ie null ).
  2. The ability to transfer ownership.

If you don't need either, you are best served with storing the Node value directly in m_head , ie:

#include <memory>

class LinkedList
{
    private:
        Node m_head;

    public:
        LinkedList(int data) : m_head(data) {}

        const Node& get_head() { return m_head; }
};

If the code example is a simplified version of the real scenario in which m_head can be "not set", we would need a different solution. std::optional would be an option. Just like std::unique_ptr , std::optional can express "not set", but in contrast to std::unique_ptr , std::optional does not support ownership transfer. If you want to prevent ownership transfer, using a type which makes such impossible is a great way to communicate your intention to your colleagues and your future self. The code would change to:

#include <memory>

class LinkedList
{
    private:
        std::optional<Node> m_head;

    public:
        LinkedList(int data) : m_head(Node(data)) {}

        // some other code which can cause m_head to be empty

        const std::optional<Node> get_head() { return m_head; }
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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