125
votes

How can I pass a std::unique_ptr into a function? Lets say I have the following class:

class A
{
public:
    A(int val)
    {
        _val = val;
    }

    int GetVal() { return _val; }
private:
    int _val;
};

The following does not compile:

void MyFunc(unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(ptr);

    return 0;
}

Why can I not pass a std::unique_ptr into a function? Surely this is the primary purpose of the construct? Or did the C++ committee intend for me to fall back to raw C-style pointers and pass it like this:

MyFunc(&(*ptr)); 

And most strangely of all, why is this an OK way of passing it? It seems horribly inconsistent:

MyFunc(unique_ptr<A>(new A(1234)));
7
There is nothing wrong with "falling back" to raw C-style pointers as long as they are non-owning raw pointers. You might prefer to write 'ptr.get()'. Although, if you don't need nullability a reference would be preferred.Chris Drew
Because the unique pointer does not have a copy constructor. Hence you cannot pass it by value, because passing by value requires making a copy. Actually that is nearly the sole and whole point of a unique_ptr.mosegui
@mosegui it's a 6 year old question with existing answers.user3690202

7 Answers

178
votes

There's basically two options here:

Pass the smart pointer by reference

void MyFunc(unique_ptr<A> & arg)
{
    cout << arg->GetVal() << endl;
}

int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(ptr);
}

Move the smart pointer into the function argument

Note that in this case, the assertion will hold!

void MyFunc(unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(move(ptr));
    assert(ptr == nullptr)
}
38
votes

You're passing it by value, which implies making a copy. That wouldn't be very unique, would it?

You could move the value, but that implies passing ownership of the object and control of its lifetime to the function.

If the lifetime of the object is guaranteed to exist over the lifetime of the call to MyFunc, just pass a raw pointer via ptr.get().

20
votes

Why can I not pass a unique_ptr into a function?

You cannot do that because unique_ptr has a move constructor but not a copy constructor. According to the standard, when a move constructor is defined but a copy constructor is not defined, the copy constructor is deleted.

12.8 Copying and moving class objects

...

7 If the class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted;

You can pass the unique_ptr to the function by using:

void MyFunc(std::unique_ptr<A>& arg)
{
    cout << arg->GetVal() << endl;
}

and use it like you have:

or

void MyFunc(std::unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

and use it like:

std::unique_ptr<A> ptr = std::unique_ptr<A>(new A(1234));
MyFunc(std::move(ptr));

Important Note

Please note that if you use the second method, ptr does not have ownership of the pointer after the call to std::move(ptr) returns.

void MyFunc(std::unique_ptr<A>&& arg) would have the same effect as void MyFunc(std::unique_ptr<A>& arg) since both are references.

In the first case, ptr still has ownership of the pointer after the call to MyFunc.

7
votes

As MyFunc doesn't take ownership, it would be better to have:

void MyFunc(const A* arg)
{
    assert(arg != nullptr); // or throw ?
    cout << arg->GetVal() << endl;
}

or better

void MyFunc(const A& arg)
{
    cout << arg.GetVal() << endl;
}

If you really want to take ownership, you have to move your resource:

std::unique_ptr<A> ptr = std::make_unique<A>(1234);
MyFunc(std::move(ptr));

or pass directly a r-value reference:

MyFunc(std::make_unique<A>(1234));

std::unique_ptr doesn't have copy on purpose to guaranty to have only one owner.

6
votes

Why can I not pass a unique_ptr into a function?

You can, but not by copy - because std::unique_ptr<> is not copy-constructible.

Surely this is the primary purpose of the construct?

Among other things, std::unique_ptr<> is designed to unequivocally mark unique ownership (as opposed to std::shared_ptr<> ).

And most strangely of all, why is this an OK way of passing it?

Because in that case, there is no copy-construction.

1
votes

Since unique_ptr is for unique ownership, if you want to pass it as argument try

MyFunc(move(ptr));

But after that the state of ptr in main will be nullptr.

-1
votes

Passing std::unique_ptr<T> as value to a function is not working because, as you guys mention, unique_ptr is not copyable.

What about this?

std::unique_ptr<T> getSomething()
{
   auto ptr = std::make_unique<T>();
   return ptr;
}

this code is working