0
votes

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; }
};
2
2 options: return by reference or just return a (non-owning) raw pointer.skeller
@skeller I'd to like re explore raw pointers. I've heard a lot of negative opinions about ever using raw pointers again since smart pointers have been introduced, but in cases like this it's okay to use a raw pointer? Are there any more cases where using raw pointers may be preferred over smart pointers?.greg
Raw pointers are completely exception safe as long as they don't own the object they are pointing to. In your case the std::unique_ptr owns it (deletes it) so returning a raw pointer is not evil and is exception safe.Galik
How do you specify that a pointer owns an object? Would a raw pointer be considered the owner of an object if it is the only pointer to that object?greg
the owing is basically "by convention". A best practice is ownership always in a unique or shared pointer. that way you "know" all raw pointers are non-owing (never call delete on it). If used that way, raw pointer are perfectly fine.skeller

2 Answers

4
votes

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; }
1
votes

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 are options other than raw vs. unique pointers.

Two important properties of std::unique_ptr are:

  1. The ability to represent a value which is not set (i.e. 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, i.e.:

#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, e.g. std::optional.

std::optional and std::unique_ptr both can represent a value being not set. However, 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; }
};