1

I need to pass a unique pointer to a derived class by reference to a function which accepts a reference to a unique pointer to the base class, like here:

#include <memory>

using namespace std;

class Base {};
class Derived : public Base {};

void foo(std::unique_ptr<Base>& d){}

int main()
{
    unique_ptr<Derived> b = make_unique<Derived>();
    foo(b);
}
  1. Why doesn't this code work? I checked out other posts like this one, and the answer seems to be "because C++ wants the types to match exactly", but why is that? What dangerous situation am I potentially creating?

  2. If I instead do this, it compiles:

    void foo(unique_ptr<Base>&& d){}
    foo(move(b));
    

    Is this a reasonable approach?

Valentin
  • 1,108
  • 8
  • 18
  • 3
    Why not just accept a `Base&`? What are you doing with the `unique_ptr` in the function? – Benjamin Lindley Oct 01 '17 at 03:44
  • @BenjaminLindley the function is a member of a static manager class and will need to add this pointer to a list> – Valentin Oct 01 '17 at 03:48
  • 1
    Then take the `unique_ptr` by value. – Benjamin Lindley Oct 01 '17 at 03:48
  • @BenjaminLindley that won't work. There's no copy constructor for unique_ptr since it's guaranteed to be unique – Valentin Oct 01 '17 at 03:49
  • 3
    @Valentin But there's a move constructor. You want `foo` to take ownership of the pointer, don't you? Take by value, and call as `foo(std::move(b))` – Igor Tandetnik Oct 01 '17 at 03:50
  • 3
    Then you move it at the call site. – Benjamin Lindley Oct 01 '17 at 03:50
  • That will work, but I'd still like to receive an answer to (1) – Valentin Oct 01 '17 at 03:53
  • 4
    As to why your original approach doesn't work: `std::uniqe_ptr` and `std::unique_ptr` are two distinct, unrelated classes. Even though `Derived` is derived from `Base`, `std::unique_ptr` is not derived from, or in other way related to, `std::uniqe_ptr`. It doesn't work for the same reason you can't pass an instance of `Apple` to a function taking `Orange&`. – Igor Tandetnik Oct 01 '17 at 03:53
  • Hmm. It's a little confusing that foo(std::shared_ptr b) will accept a pointer to a derived class, but foo(std::shared_ptr& b) won't. But now that I'm thinking about it it makes sense. – Valentin Oct 01 '17 at 04:00
  • Please explain **what you want to do** not how you believe you should do it! – curiousguy Oct 04 '17 at 23:20

2 Answers2

2

What dangerous situation am I potentially creating?

Imagine the following implementation for foo:

void foo(std::unique_ptr<Base>& d){
    d.reset(new Base);
}

You now have a std::unique_ptr<Derived> pointing to an object which is not of type Derived, and the compiler couldn't give you any kind of warning about it.

The correct solution to your problem, as explained in the comments, is to take a std::unique_ptr<Base> by value, and move it at the call site.

void foo(std::unique_ptr<Base> d) {
    // move d to your list
}

int main() {
    unique_ptr<Derived> b = make_unique<Derived>();
    foo(std::move(b));
}
Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
0

A simple static_cast from Derived to Base, with an appropriate release call (to transfer the ownership of the resource to the newly created pointer) should work fine.

int main()
{
    unique_ptr<Derived> b = make_unique<Derived>();
    std::unique_ptr<Base> basePointer(static_cast<Base*>(b.release()));
    foo(basePointer);
}
Abhay Sibal
  • 129
  • 1
  • 12
  • You think `foo(std::move(b));` is superfluous, but `std::unique_ptr basePointer(static_cast(b.release())); foo(basePointer);` is not? – Benjamin Lindley Oct 01 '17 at 04:16
  • Technically both are same. Edited my answer. I had a nagging feeling that the move might be an overkill...and could cause exceptions in case one misses out on the implicit conversion of b, and uses it down the line. (The release call makes it kind of explicit). – Abhay Sibal Oct 01 '17 at 04:38