164

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)));
Guy Avraham
  • 3,482
  • 3
  • 38
  • 50
user3690202
  • 3,844
  • 3
  • 22
  • 36
  • 9
    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 Jun 18 '15 at 04:51
  • 1
    @mosegui it's a 6 year old question with existing answers. – user3690202 Jun 24 '21 at 01:19

8 Answers8

232

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)
}
Bill Lynch
  • 80,138
  • 16
  • 128
  • 173
  • @VermillionAzure: The feature you are referring to is normally called noncopyable, not unique. – Bill Lynch Jun 18 '15 at 02:35
  • 2
    Thanks. In this case I wanted to create an instance, perform some actions on it and then pass off ownership to someone else, which move() seems perfect for. – user3690202 Jun 18 '15 at 02:42
  • 9
    You should only pass a `unique_ptr` by reference if the function might or might-not move from it. And then it should be an rvalue reference. To observe an object without requiring anything about its ownership semantics, use a reference like `A const&` or `A&`. – Potatoswatter Jun 18 '15 at 03:56
  • 32
    Listen to Herb Sutters talk on CppCon 2014. He strongly discourages, to pass references to unique_ptr<> . The essence is, that you should just use smart pointers like unique_ptr<> or shared_ptr<> when you deal with ownership. See www.youtube.com/watch?v=xnqTKD8uD64 Here you can find the slides https://github.com/CppCon/CppCon2014/tree/master/Presentations/Back%20to%20the%20Basics!%20Essentials%20of%20Modern%20C%2B%2B%20Style – schorsch_76 Jun 18 '15 at 05:43
  • Is `assert(ptr == nullptr)` necessary? – Eliad Jun 18 '15 at 07:32
  • 4
    @Furihr: It's just a way of showing what the value of `ptr` is after the move. – Bill Lynch Jun 18 '15 at 14:35
  • @AdamGetchell the fact that you got segfaults doesn't mean std::move() must always be avoided, it means you aren't completely grokking what unique_ptr is and what std::move is for, and are using them incorrectly. "Eradicating std::move() wherever you find it" is just as much of a footgun as keeping it and using it incorrectly. You should use it where you need it, and don't use it where you don't need it. – MadScientist Mar 05 '19 at 13:52
  • @Potatoswatter *use a reference like A const& or A&* - so on the caller side you mean to dereference the unique_ptr like `MyFunc(*ptr)` to pass a reference to the object, do I understand right? – bloody Jan 14 '22 at 10:26
  • 1
    @bloody yes. A function which only observes `A` doesn’t need to know about the ownership, so the smart pointer is an external concern. – Potatoswatter Jan 14 '22 at 10:33
  • In your first code example, can you pass `arg` as a `const unique_ptr`, and not just a regular unique_ptr? Or is that forbidden? – Raleigh L. Sep 15 '22 at 19:25
  • https://stackoverflow.com/questions/8114276/how-do-i-pass-a-unique-ptr-argument-to-a-constructor-or-a-function?rq=1 – Anurag Singh Apr 25 '23 at 13:33
54

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

Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • Is the overhead of passing a ```unique_ptr``` by reference significant to warrant this call to ```ptr.get()``` and exposing the raw pointer? – User 10482 Feb 15 '21 at 00:51
  • 1
    @User10482 See the comment on the question itself: "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." and the comment about Herb Sutter discouraging passing references to unique_ptr in the accepted answer. – Mark Tolonen Feb 15 '21 at 01:00
  • Actually passing a unique_ptr by value doesn't imply a copy and that's the best way to do it. Consider that a unique_ptr only has the move constructor so the caller has to use the "move" function or create the unique_ptr in place. This can be a good reference: http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rr-uniqueptrparam – Triskeldeian Jun 01 '21 at 11:42
  • 2
    @Triskeldeian That was the point of the question. Passing a `unique_ptr` by value the way the OP was doing it *does* imply a copy and didn't work, obviously due to no copy constructor. I *did* mention that they could move the value but that passes ownership. – Mark Tolonen Jun 01 '21 at 16:48
23

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.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
7

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.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
6

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.

0x6773
  • 1,116
  • 1
  • 14
  • 33
Nielk
  • 760
  • 1
  • 6
  • 22
  • Thanks for your answer. Just out of interest, can you explain why the last way of passing it isn't using the copy constructor? I would have thought that the use of the unique_ptr's constructor would generate an instance on the stack, which would be copied into the arguments for MyFunc() using the copy constructor? Though I admit my recollection is a little vague in this area. – user3690202 Jun 18 '15 at 02:47
  • 2
    As it is an rvalue, the move-constructor is called instead. Though your compiler will certainly optimize this out anyway. – Nielk Jun 18 '15 at 02:53
1

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.

0x6773
  • 1,116
  • 1
  • 14
  • 33
  • 6
    "will be undefined" - no, it will be null, or `unique_ptr` would be rather useless. – T.C. Jun 18 '15 at 02:37
0

To piggyback the existing answers, C++ smart pointers are strongly related to the concept of ownership. Your code should clearly express your inensions related to passing or not the ownership to another component, i.e. function, thread or so, that's why there are unique, shared and weak pointers. The unique pointer already suggests that only one component has the ownership of that pointer at one moment, so that unique ownership cannot be shared, only moved. Namely the owner that currently owns the pointer will destroy it at its own will when getting out of context, usually at the end of the block or in a destructor. Passing an object to another function might imply sharing or moving the ownership. If you are sure that the owner don't get out of the context while calling the function by reference, calling by reference is possible, but it's just ugly. It adds some preconditions and postconditions and will increase the immobility of your code while it also breaks the smartness of the pointer which tuns into a more or less raw pointer. For example, changing the "function" to start and execute its own stuff in another thread is not longer possible. As passing by reference is still an option in some very contained parts of your code, i.e. looking for saving extra overhead of moving back and forth from unique to shared pointers, I would highly avoid it, especially in public interfaces.

-1

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

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Riste
  • 11