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