Search code examples
c++templatessmart-pointers

Pass raw pointer to function that accepts unique_ptr as parameter


I'm trying to write a function that accepts unique_ptr to classes derived from Base as a parameters (to indicate ownership transfer). I've come up with the following code:

#include <memory>
#include <type_traits>

class Base
{
  public:
    Base() {}
};

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


void foo(std::unique_ptr<Derived> _data) {}

template <class T>
void boo(std::unique_ptr<T> _data) {
    static_assert(std::is_convertible<T*, Base*>::value,
                  "Only classes derived from Base can be used as "
                  "parameter for boo");
}


int main() { foo(new Derived); }

But when I try to compile it, I get an error telling me that could not convert ‘(operator new(1ul), (<statement>, ((Derived*)<anonymous>)))’ from ‘Derived*’ to ‘std::unique_ptr<Derived>’ if I use foo and the same (plus some template details) when I use boo. If I understood everything correctly, what I'm being told is that there is no constructor for std::unique_ptr<Derived> that could accept pointer for Derived. But when I looked on cppreference page for unqiue_ptr constructor, I saw that there is such constructor (number 3-4).

The question is: How can I make a function that accepts unique_ptr as parameter and call it with raw pointer.

P.S. I'm trying to write a function that accepts unique_ptr because I want to indicate that ownership of the parameter will be transferred. If you know how to write a function with a signature that could clearly and unambiguously show that ownership is being transferred, I would also accept this as an answer. This function can either template with static_assert or with Base pointer as a parameter.

P.S. The main problem is how to make foo(new Derived). I know that I can use boo(std::make_unique<Derived>()) (in that case everything works) but I also really want to know why the example with raw pointer does not work, because I don't know how to indicate that I'm "stealing" ownership for raw pointer.

P.S Example of usage (with raw pointers) (DOES NOT WORK)

Derived* derived_1 = new Derived;
// Do something with derived_1
boo(derived_1); // Pass to function and don't worry about `delete`
                // because you know that ownership has been
                // transferred and no longer is your concern

And with smart pointers

void foo_2(std::unique_ptr<Derived>& _data) {
     boo(std::move(_data)); 
}

std::unique_ptr<Derived> derived_2 = std::make_unique<Derived>();
// Do something with derived_2
foo(std::move(derived_2)); // Have to use `std::move`
                           // or
foo_2(derived_2);

Second one (omitting need for new function) is not as simple as first one (even though I should admit that the difference is not so big (maybe 2x typing))


Solution

  • Overloads 3 and 4 do not apply here. Their signature is

    unique_ptr( pointer p, /* see below */ d1 ) noexcept;
    
    unique_ptr( pointer p, /* see below */ d2 ) noexcept;
    

    and they take a second parameter that is a custom deleter. The only overload that applies is 2 which is

    explicit unique_ptr( pointer p ) noexcept;
    

    Since it is marked as explicit it will not implicitly convert the pointer to a unique_ptr. This is a safety issues and it is correct to make it explicit. If it was not explicit then

    void foo(std::unique_ptr<int> _data) {}
    
    int main()
    {
        int bar;
        foo(&bar);
    }
    

    would compile but it would be undefined behavior as _data would try to delete the pointer when it was destroyed at the end of foo.