unique_ptr to a derived class as an argument to a function that takes a unique_ptr to a base class
Solution 1
You have three options:
-
Give up ownership. This will leave your local variable without access to the dynamic object after the function call; the object has been transferred to the callee:
f(std::move(derived));
-
Change the signature of
f
:void f(std::unique_ptr<Derived> const &);
-
Change the type of your variable:
std::unique_ptr<base> derived = std::unique_ptr<Derived>(new Derived);
Or of course just:
std::unique_ptr<base> derived(new Derived);
Or even:
std::unique_ptr<base> derived = std::make_unique<Derived>();
-
Update: Or, as recommended in the comments, don't transfer ownership at all:
void f(Base & b); f(*derived);
Solution 2
I had option #1 of the accepted answer and I still had the same compile error. I banged my head on the wall for over an hour and I finally realized that I had
class Derived : Base {};
instead of
class Derived : public Base {};
Solution 3
A possibile solution is to change the type of the argument to be a Base const*
, and pass derived.get()
instead. There is no transfer of ownership with unique_ptr const<Base>&
(and the unique_ptr
is not being modified), so changing to a Base const*
does not change the meaning.
Herb Sutter discusses passing smart pointer arguments at length in Smart Pointer Parameters. An extract from the linked article refers to this exact situation:
Passing a
const unique_ptr<widget>&
is strange because it can accept only eithernull
or awidget
whose lifetime happens to be managed in the calling code via aunique_ptr
, and the callee generally shouldn’t care about the caller’s lifetime management choice. Passingwidget*
covers a strict superset of these cases and can accept “null
or awidget
” regardless of the lifetime policy the caller happens to be using.
svick
Updated on July 08, 2022Comments
-
svick almost 2 years
I'm trying to use a
unique_ptr
to derived class in a function that takes aunique_ptr
to a base class. Something like:class Base {}; class Derived : public Base {}; void f(unique_ptr<Base> const &base) {} … unique_ptr<Derived> derived = unique_ptr<Derived>(new Derived); f(derived);
If I understand this answer correctly, this code should work, but it causes the following compile errors:
error C2664: 'f' : cannot convert parameter 1 from 'std::unique_ptr<_Ty>' to 'const std::unique_ptr<_Ty> &'
IntelliSense: no suitable user-defined conversion from "std::unique_ptr<Derived, std::default_delete<Derived>>" to "const std::unique_ptr<Base, std::default_delete<Base>>" exists
If I change
f
to takeunique_ptr<Derived> const &derived
, it works fine, but that's not what I want.Am I doing something wrong? What can I do to work around this?
I'm using Visual Studio 2012.
-
svick almost 11 yearsIn that case, I'm considering going with 4. Use
shared_ptr
. -
ltjax almost 11 yearsHow about just using a reference instead of a unique_ptr for the function call?
-
Jonathan Wakely almost 11 years@svick, why pass in a smart pointer to the function if it doesn't take ownership of the pointer? That's not what smart pointers are for.
-
svick almost 11 yearsDoesn't
Base const*
mean that the function could store the pointer somewhere? I though smart pointers are trying to avoid that. -
svick almost 11 years@ltjax How exactly would I do that?
-
hmjd almost 11 years@svick, the function could store
base.get()
just as easily (get()
isconst
member function). -
svick almost 11 years@JonathanWakely So, what else am I supposed to do? Because I don't like any of the alternatives in this answer.
-
etam1024 almost 11 years@svick No. Smart pointers are to avoid memory leaks by making clear who is the owner of an object. If you pass raw pointer as argument to a function you just give access to read/modify it. Smart pointers are to avoid using raw
new
anddelete
, not pointers at all. -
metal about 10 years@KerrekSB, Say one has a factory function:
std::unique_ptr<Base> MakeDerived() { auto p = std::unique_ptr<Derived>( new Derived() ); /*...*/ return p; }
My intuition says I shouldn't needstd::move(p)
for the return, but my compilers tell me I do. Is this intended behavior or a defect in the standard? -
metal about 10 yearsDone here.
-
Kerrek SB about 10 years@metal: Great, thanks - yeah, important detail, the return types have to match. I missed that. Good stuff. You could have said
return std::unique_ptr<Base>(std::move(p))
, I suppose. -
Zitrax over 7 years"This will destroy the object at the end of the function call": But we don't know what
f()
is doing (assuming that it's actually not empty) - it might pass the ownership elsewhere. Isn't all we can say that we give up or transfer the ownership ? -
Kerrek SB over 7 years@Zitrax: You're right, that isn't phrased very well. Let me edit it.