Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I transfer ownership of a unique_ptr when returning a tuple?

I'm trying to return a tuple, one of the elements of which is a std::unique_ptr. I want to transfer the ownership of the unique_ptr to the caller. How do I do this?

#include <tuple>
#include <memory>
#include <iostream>

using namespace std;

class B
{
public:
    B(int i) : i_(i) {}

    int getI() const { return i_; }
private:
   int i_;
};

tuple<unique_ptr<B>, int>
getThem()
{
    unique_ptr<B> ptr(new B(10));
    return make_tuple(ptr, 50);
}

int
main(int argc, char *argv[])
{

    unique_ptr<B> b;
    int got = 0;

    tie(b, got) = getThem();

    cout << "b: " << b->getI() << endl;
    cout << "got: " << got << endl;

    return 0;
}

This fails to compile because the copy constructor of unique_ptr is deleted, for obvious reasons. But how to I indicate I want to move the unique_ptr into the tie?

like image 333
firebush Avatar asked Oct 09 '15 05:10

firebush


People also ask

What happens when you return a unique_ptr?

If a function returns a std::unique_ptr<> , that means the caller takes ownership of the returned object. class Base { ... }; class Derived : public Base { ... }; // Foo takes ownership of |base|, and the caller takes ownership of the returned // object.

How do I transfer ownership of a unique pointer?

In C++11 we can transfer the ownership of an object to another unique_ptr using std::move() . After the ownership transfer, the smart pointer that ceded the ownership becomes null and get() returns nullptr.

Can you assign a unique_ptr?

This means that a unique_ptr can be assigned to the unique_ptr returned from a function, but you can't assign one declared unique_ptr to another. But if you invoke a move assignment (with std::move) you can assign them, but the lhs unique_ptr now owns the object and the rhs now owns nothing.

Is unique_ptr null after move?

Yes, you can compare it to nullptr after the move and it is guaranteed to compare equal. This is clearly true after calling release().


2 Answers

Essentially you just need to explicitly move the non-copyable types into the tuple, thus using std::move. std::tuple has the appropriate constructors to copy and move the types internally (the move being appropriate here).

As follows;

#include <tuple>
#include <memory>
#include <iostream>

using namespace std;

class B
{
public:
    B(int i) : i_(i) {}

    int getI() const { return i_; }
private:
    int i_;
};

tuple<unique_ptr<B>, int>
getThem()
{
    unique_ptr<B> ptr(new B(10));
    return make_tuple(std::move(ptr), 50); // move the unique_ptr into the tuple
}

int
main(int argc, char *argv[])
{
    unique_ptr<B> b;
    int got = 0;

    tie(b, got) = getThem();

    cout << "b: " << b->getI() << endl;
    cout << "got: " << got << endl;

    return 0;
}
like image 68
Niall Avatar answered Nov 10 '22 16:11

Niall


Use std::move to invoke the move operator instead.

return make_tuple(std::move(ptr), 50);
like image 22
gigaplex Avatar answered Nov 10 '22 14:11

gigaplex