unique_ptr to a derived class as an argument to a function that takes a unique_ptr to a base class

C++Visual Studio-2012C++11Unique Ptr

C++ Problem Overview


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.

C++ Solutions


Solution 1 - C++

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);

Solution 2 - C++

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 - C++

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.

Solution 4 - C++

Another way is change the signature of f and use it in a slightly different way:

void f(Base* base_ptr) {
    // take ownership inside the function
    std::unique_ptr<Base> base {base_ptr};
    // ...
}

// ...
auto derived = std::make_unique<Derived>();
f(derived.release());  // release ownership

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsvickView Question on Stackoverflow
Solution 1 - C++Kerrek SBView Answer on Stackoverflow
Solution 2 - C++DavidView Answer on Stackoverflow
Solution 3 - C++hmjdView Answer on Stackoverflow
Solution 4 - C++AlQuemistView Answer on Stackoverflow