Search code examples
c++c++11unique-ptrstdmovedeleted-functions

Initializing unique_ptr causes an "error: use of deleted function" even though it's "std::move"ed


I am writing code that passes a std::unique_ptr through a few layers that look bad, but I don't have a choice but pass it all along for now.

The problem is that I am getting an error when I try to pass the std::unique_ptr to a constructor of the Provider class. At the point where Child::function1() is called, Child has been initialized/constructed with parameters of Impl& and std::unique_ptr<Retriever> from somewhere else.

Could someone please tell me why I'm getting this error, and how to get this work? I wonder if it's because of the inheritance happening, but I can't figure it out what else to do besides moving to get this work... (apart from something like not to pass this ptr through all these classes that seem to look similar with the same constructors... lol)

// child.cpp
Child::Child(Impl& child_impl, std::unique_ptr<Retriever> child_retriever_up)
: Parent(child_impl, std::move(child_retriever_up))
{}

T::Y Child::function1() const
{
    **Provider provider(d_impl, d_retriever_up);** 
    // these d_impl and d_retriever are in Parent class.
    ...
    return Y;
}

// child.h
class Child : public Parent {
public:
    // creators
    explicit Child(Impl& child_impl, std::unique_ptr<Retriever> child_retriever_up);
    ~Child() override = default;
    // accessors
    T::Y Child::function1() const;
private:
    Child(const Child&) = delete;
    Child& operator=(const Child&) = delete;
// parent.cpp
Parent::Parent(Impl& impl, std::unique_ptr<Retriever> retriever_up)
: d_impl(impl), d_retriever_up(std::move(retriever_up))
{}

// parent.h
class Parent {
public:
    // creators
    explicit Parent(Impl& impl, std::unique_ptr<Retriever> retriever_up);
    virtual ~Parent() = default;
    // copy constructor
    Parent(const Parent& parent) 
    : d_context(parent.d_impl)
    , d_retriever_up(std::move(*parent.d_retriever_up))
    {
    }
    // data
    Impl& d_impl;
    std::unique_ptr<Retriever> d_retriever_up;
private:
    //Parent(const Parent&) = delete;
    Parent& operator=(const Parent&) = delete;
// provider.cpp
Provider::Provider(Impl& impl, std::unique_ptr<Retriever> retriever_up)
: d_impl(impl)
, d_retriever(std::move(retriever_up))
{}

// provider.h
class Provider {
public:
    Provider(Impl& context, std::unique_ptr<Retriever> retriever_up);
    //copy contstructor
    Provider(const Provider& provider) 
    : d_impl(provider.d_impl)
    , d_retriever(std::move(*provider.d_retriever))
    {
    }

private:
    Impl& d_impl;
    std::unique_ptr<Retriever> d_retriever;
}

Solution

  • In Child::function1(), you are passing d_retriever_up by value to the Provider constructor. That requires making a copy of d_retriever_up, which is impossible as it is a unique_ptr that has delete'd its copy constructor, hence the error.

    You need to use std::move() to move d_retriever_up into Provider. You claim to be doing that, but you are not, at least not everywhere it is needed. Moving the caller's unique_ptr into the Provider constructor's retriever_up parameter is a separate operation than moving the retriever_up parameter into the Provider's d_retriever class member.

    However, after you fix that, you will run into another problem. Child::function1() is marked asconst, which makes access to d_retriever_up be const in that context, so function1() can't modify d_retriever_up, including moves (since moving a unique_ptr sets its held pointer to nullptr).

    So, to make the code work, you need to drop the const and add std::move():

    T::Y Child::function1()
    {
        Provider provider(d_impl, std::move(d_retriever_up));
        ...
        return Y;
    }
    

    That being said, moving d_retriever_up is questionable in your design. You might need to consider using std::shared_ptr instead.