81

I'm trying to use a unique_ptr to derived class in a function that takes a unique_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 take unique_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.

Community
  • 1
  • 1
svick
  • 214,528
  • 47
  • 357
  • 477

3 Answers3

83

You have three options:

  1. 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));
    
  2. Change the signature of f:

    void f(std::unique_ptr<Derived> const &);
    
  3. 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>();
    
  4. Update: Or, as recommended in the comments, don't transfer ownership at all:

    void f(Base & b);
    
    f(*derived);
    
Karl Nicoll
  • 14,491
  • 3
  • 46
  • 60
Kerrek SB
  • 428,875
  • 83
  • 813
  • 1,025
  • 1
    In that case, I'm considering going with 4. Use `shared_ptr`. – svick Jul 04 '13 at 15:37
  • 4
    How about just using a reference instead of a unique_ptr for the function call? – ltjax Jul 04 '13 at 15:51
  • 17
    @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. – Jonathan Wakely Jul 04 '13 at 15:56
  • @ltjax How exactly would I do that? – svick Jul 04 '13 at 16:05
  • @JonathanWakely So, what else am I supposed to do? Because I don't like any of the alternatives in this answer. – svick Jul 04 '13 at 16:05
  • @KerrekSB, Say one has a factory function: `std::unique_ptr MakeDerived() { auto p = std::unique_ptr( new Derived() ); /*...*/ return p; }` My intuition says I shouldn't need `std::move(p)` for the return, but my compilers tell me I do. Is this intended behavior or a defect in the standard? – metal Feb 25 '14 at 13:22
  • Done [here](http://stackoverflow.com/questions/22018115/converting-stdunique-ptrderived-to-stdunique-ptrbase). – metal Feb 25 '14 at 15:05
  • 2
    @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(std::move(p))`, I suppose. – Kerrek SB Feb 25 '14 at 15:26
  • _"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 ? – Zitrax Jan 10 '17 at 10:11
  • @Zitrax: You're right, that isn't phrased very well. Let me edit it. – Kerrek SB Jan 10 '17 at 10:32
18

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 {};
David
  • 1,929
  • 19
  • 15
12

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 either null or a widget whose lifetime happens to be managed in the calling code via a unique_ptr, and the callee generally shouldn’t care about the caller’s lifetime management choice. Passing widget* covers a strict superset of these cases and can accept “null or a widget” regardless of the lifetime policy the caller happens to be using.

hmjd
  • 113,589
  • 17
  • 194
  • 245
  • 1
    Doesn't `Base const*` mean that the function could store the pointer somewhere? I though smart pointers are trying to avoid that. – svick Jul 04 '13 at 16:04
  • @svick, the function could store `base.get()` just as easily (`get()` is `const` member function). – hmjd Jul 04 '13 at 16:05
  • 2
    @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` and `delete`, not pointers at all. – etam1024 Jul 05 '13 at 00:08