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;
}
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.