Alpharius
Alpharius

Reputation: 509

Сreate a shared_ptr from a unique_ptr containing the structure

I have a class containing such a structure:

class Store {

private:
    struct pointNode {
        T data;
        std::unique_ptr<pointNode> next;

        pointNode():data(std::move(T{})),next(std::move(std::unique_ptr<pointNode>{})){}
        pointNode(T n_data, std::unique_ptr<pointNode> ptr) : data(std::move(n_data)), next(std::move(ptr)) {}
    };
    std::unique_ptr<pointNode> head;
public:
     decltype(auto) Begin() ;
};

I need to create a Begin function that will get a pointer to head. I wrote something like this:

template<typename T>
decltype(auto) Stack<T>::Begin() 
{
    std::shared_ptr<pointNode> sh_ptr=std::make_shared<pointNode>(head);
    return std::move(sh_ptr);
}

That is, create a shared_ptr and return it from the function, then go through the list. But:

std::shared_ptr<pointNode> sh_ptr=std::make_shared<pointNode>(head);

returns error C2664

Error C2664 "Stack<int>::pointNode::pointNode(Stack<int>::pointNode &&)": 
argument 1 cannot be converted  from "std::unique_ptr<Stack<int>::pointNode,std::default_delete<Stack<int>::pointNode>>" 
in "const Stack<int>::pointNode &"      

I tried to remove decltype (auto), but everything is the same .Maybe someone understands what I'm doing wrong .I would be happy to help, thank you

Upvotes: 0

Views: 255

Answers (2)

Ted Lyngmo
Ted Lyngmo

Reputation: 117188

unique_ptrs claim unique ownership. The clean way to transfer ownership to a shared_ptr would be to move it. So if you have

std::unique_ptr<pointNode> head;

then

std::shared_ptr<pointNode> sh_ptr( std::move(head) );

This is however a design flaw.

You do not want to transfer ownership of the container's elements. Here you actually want to share the raw pointer with the iterator. Any iterator that you have should be able to use that raw pointer - but not destroy the element it points at.

The first draft of a simple forward list type of container would be something like the below - and nowhere should a unique_ptr or shared_ptr be used:

template<class T>
class Store {
private:
    struct Node {
        T data;
        Node* next;
    };

    Node* head = nullptr;

public:
    struct iterator {
        iterator(Node* x = nullptr) : current(x) {}

        T& operator*() { return current->data; }
        T* operator->() { return &current->data; }
        
        iterator& operator++() { current = current->next; return *this; }

        bool operator!=(const iterator& rhs) const {
            return current != rhs.current;
        }

    private:
        Node* current;
    };

    ~Store() {
        Node* next;
        while(head) {
            next = head->next;
            delete head;
            head = next;
        }
    }
    iterator begin() { return head; }
    iterator end() { return nullptr; }
};

Demo

Upvotes: 1

Aykhan Hagverdili
Aykhan Hagverdili

Reputation: 29955

unique_ptr cannot share the data with shared_ptr. That's the point of a unique pointer. But if you want to transfer the ownership, you can do it like so:

#include <string>
#include <memory>

int main()
{
  std::unique_ptr<std::string> up( new std::string("Hello, World!\n") );
  // up owns the object

  // Transfer ownership
  std::shared_ptr<std::string> sp( up.release() );
  // sp owns the object, up is empty.
}

Having said that, returning a shared_ptr from the begin() function is likely not what you want. I would guess that you want to return a reference or an iterator from that function. Here's how you can return a reference:

template<typename T>
pointNode& Stack<T>::begin() 
{
    return *head;
}

Note that, for this to work like STL iterators, you need to create a new iterator type. begin returning a refence to a private node class is not a great interface and leaks implementation details.

Upvotes: 1

Related Questions