How to Pass a Unique_Ptr Argument to a Constructor or a Function

How do I pass a unique_ptr argument to a constructor or a function?

Here are the possible ways to take a unique pointer as an argument, as well as their associated meaning.

(A) By Value

Base(std::unique_ptr<Base> n)
: next(std::move(n)) {}

In order for the user to call this, they must do one of the following:

Base newBase(std::move(nextBase));
Base fromTemp(std::unique_ptr<Base>(new Base(...));

To take a unique pointer by value means that you are transferring ownership of the pointer to the function/object/etc in question. After newBase is constructed, nextBase is guaranteed to be empty. You don't own the object, and you don't even have a pointer to it anymore. It's gone.

This is ensured because we take the parameter by value. std::move doesn't actually move anything; it's just a fancy cast. std::move(nextBase) returns a Base&& that is an r-value reference to nextBase. That's all it does.

Because Base::Base(std::unique_ptr<Base> n) takes its argument by value rather than by r-value reference, C++ will automatically construct a temporary for us. It creates a std::unique_ptr<Base> from the Base&& that we gave the function via std::move(nextBase). It is the construction of this temporary that actually moves the value from nextBase into the function argument n.

(B) By non-const l-value reference

Base(std::unique_ptr<Base> &n)
: next(std::move(n)) {}

This has to be called on an actual l-value (a named variable). It cannot be called with a temporary like this:

Base newBase(std::unique_ptr<Base>(new Base)); //Illegal in this case.

The meaning of this is the same as the meaning of any other use of non-const references: the function may or may not claim ownership of the pointer. Given this code:

Base newBase(nextBase);

There is no guarantee that nextBase is empty. It may be empty; it may not. It really depends on what Base::Base(std::unique_ptr<Base> &n) wants to do. Because of that, it's not very evident just from the function signature what's going to happen; you have to read the implementation (or associated documentation).

Because of that, I wouldn't suggest this as an interface.

(C) By const l-value reference

Base(std::unique_ptr<Base> const &n);

I don't show an implementation, because you cannot move from a const&. By passing a const&, you are saying that the function can access the Base via the pointer, but it cannot store it anywhere. It cannot claim ownership of it.

This can be useful. Not necessarily for your specific case, but it's always good to be able to hand someone a pointer and know that they cannot (without breaking rules of C++, like no casting away const) claim ownership of it. They can't store it. They can pass it to others, but those others have to abide by the same rules.

(D) By r-value reference

Base(std::unique_ptr<Base> &&n)
: next(std::move(n)) {}

This is more or less identical to the "by non-const l-value reference" case. The differences are two things.

  1. You can pass a temporary:

    Base newBase(std::unique_ptr<Base>(new Base)); //legal now..
  2. You must use std::move when passing non-temporary arguments.

The latter is really the problem. If you see this line:

Base newBase(std::move(nextBase));

You have a reasonable expectation that, after this line completes, nextBase should be empty. It should have been moved from. After all, you have that std::move sitting there, telling you that movement has occurred.

The problem is that it hasn't. It is not guaranteed to have been moved from. It may have been moved from, but you will only know by looking at the source code. You cannot tell just from the function signature.

Recommendations

  • (A) By Value: If you mean for a function to claim ownership of a unique_ptr, take it by value.
  • (C) By const l-value reference: If you mean for a function to simply use the unique_ptr for the duration of that function's execution, take it by const&. Alternatively, pass a & or const& to the actual type pointed to, rather than using a unique_ptr.
  • (D) By r-value reference: If a function may or may not claim ownership (depending on internal code paths), then take it by &&. But I strongly advise against doing this whenever possible.

How to manipulate unique_ptr

You cannot copy a unique_ptr. You can only move it. The proper way to do this is with the std::move standard library function.

If you take a unique_ptr by value, you can move from it freely. But movement doesn't actually happen because of std::move. Take the following statement:

std::unique_ptr<Base> newPtr(std::move(oldPtr));

This is really two statements:

std::unique_ptr<Base> &&temporary = std::move(oldPtr);
std::unique_ptr<Base> newPtr(temporary);

(note: The above code does not technically compile, since non-temporary r-value references are not actually r-values. It is here for demo purposes only).

The temporary is just an r-value reference to oldPtr. It is in the constructor of newPtr where the movement happens. unique_ptr's move constructor (a constructor that takes a && to itself) is what does the actual movement.

If you have a unique_ptr value and you want to store it somewhere, you must use std::move to do the storage.

c++ unique_ptr argument passing

Yes, this is how it should be done. You are explicitly transferring ownership from main to A. This is basically the same as your previous code, except it's more explicit and vastly more reliable.

How can I pass std::unique_ptr into a function

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

How to use unique_ptr in constructor?

A unique pointer represents at most one owner of its pointee. Therefore, a unique pointer cannot be copied. It can however be moved, which transfers the (potential) ownership to the target of the move and leaves the source of the move null (i.e. not owning anything).

Given classes Xp, Xl and Xx, each with a member std::unique_ptr<T> p_;, the following constructors all work:

Xp(std::unique_ptr<T> p) : p_(std::move(p)) {}
Xp(std::unique_ptr<T> p) { p_ = std::move(p); }

Xl(std::unique_ptr<T> & p) : p_(std::move(p)) {}
Xl(std::unique_ptr<T> & p) { p_ = std::move(p); }

Xx(std::unique_ptr<T> && p) : p_(std::move(p)) {}
Xx(std::unique_ptr<T> && p) { p_ = std::move(p); }

Only Xp and Xx have sensible constructors, though. They can be used as follows:

{
Xp xp(std::make_unique<T>(a, b ,c));
Xx xx(std::make_unique<T>(a, b ,c));
}
{
auto p = std::make_unique<T>(a, b ,c);
// Xp xp(p); // Error, cannot duplicate p!
Xp xp(std::move(p));
}
{
auto p = std::make_unique<T>(a, b ,c);
// Xx xx(p); // Error, cannot duplicate p!
Xx xx(std::move(p));
}

On the other hand, the constructor of Xl is weird and surprising:

// Xl xl(std::make_unique<T>(a, b ,c));  // Error, cannot bind to temporary
auto p = std::make_unique<T>(a, b ,c);
Xl xp(p); // OK?!?
assert(p == nullptr); // grand theft autoptr!

Passing std::unique_ptr to constructor to take ownership

Binding to a reference requires one less move:

void f(std::unique_ptr<T>&& p) { g(std::move(p)); }

f(std::make_unique<T>()); // no move, g binds directly to the temporary

Binding to an object parameter requires one actual move:

void f(std::unique_ptr<T> p) { g(std::move(p)); }

f(std::make_unique<T>()); // p constructed (= moved) from temporary,
// g binds to p

The extra move involves one pointer copy, one setting of a pointer to null, and one destructor with a condition check. This is not a big cost, and the question of which style to use depends on the kind of code you're writing:

The more leaf your code is and the less it is used, the more the simple version with by-value passing buys you simplicity. Conversely, the more library your code is and the less you know your users, the more there is value in not imposing avoidable cost, no matter how small.

You decide.

How can I use Unique_ptrs with an class object having std::function as argument in the constructor

auto bPtr = std::unique_ptr<B>(new B( std::function< std::unique_ptr<A>() > () ));
// need to close the template ^
// need to construct an instance of ^^

You get the same a bit simpler by using std::make_unique:

auto bPtr = std::make_unique<B>( std::function< std::unique_ptr<A>() >() );
// still as above ^^^

Edit: Adjustment to new question version:

You have not yet provided a copy constructor – so you need to store the lambda instead of creating a B instance:

auto l = []() { return std::make_unique<A>(); };
auto bPtr = std::unique_ptr<B>(new B(l));
// or:
auto ptr = std::make_unique<B>(l);

Note that this new edited version provides a factory function to the std::function object (the lambda!), while the initial variants constructed an empty one without function stored inside, so not callable!

Cannot pass std::unique_ptr in std::function

The std::function requires the function object to be Copy-Constructible, so you can't expect a lamdba to be moved to it. On initialization, it attempts to copy the lambda and so the std::unique_ptr with it, which is a member of this lambda, and, expectedly, fails to do so. What you can do is store your lambda in a variable and pass it to function that accepts const std::function& using std::ref like that:

        void foo(const std::function<void()>& f); // function declaration
auto a = [h = std::move(handle)]() mutable
{
std::cout << *h << std::endl;
};
foo(std::ref(a));

This is a related question with much more detailed answers: How to create an std::function from a move-capturing lambda expression?

passing reference argument to function taking universal reference of unique_ptr

Your compiler complains because make_unique calls new on the type you are trying to instantiate, effectively copying the existing object. Of course, it can't do that, as the class is abstract.

Unless you have a way to guarantee that the reference passed to MyUserClass is to a dynamic ("heap") variable (and its pointer is not owned, etc.), you cannot just capture its pointer in unique_ptr and then release it after doSomethingElse. Even if this is guaranteed, for all you know, doSomethingElse could still try to delete the pointer itself (unless you know exactly what it does). As you pass it by rvalue reference, the object may not as well be valid after doSomethingElse returns.

That means you have to make a copy of the object. You can't make a plain copy though. You need to do a copy of the entire, non-abstract, underlying object.

If you are allowed to change signature of the classes, adding a virtual clone method would to the trick.

If not, a not-absolutely-terrible workaround could be (you need to know beforehand all the types that inherit MyAbstractClass though) switch(typeid(mac)) and dynamic_cast.

unique_ptr to a derived class as an argument to a function that takes a unique_ptr to a base class and take owenership

The function must accept the unique_ptr by value:

  void addElement (std::unique_ptr<Base> base)
{
myVector.push_back(std::move(base));
}

and then call the member function like this:

MyClass myClass;

auto b = std::make_unique<Base>();
myClass.addElement(std::move(b));

auto d = std::make_unique<Derived>();
myClass.addElement(std::move(d));

or even better avoid the variables because you cannot use them anyway after the function call:

MyClass myClass;

myClass.addElement(std::make_unique<Base>());

myClass.addElement(std::make_unique<Derived>());


Related Topics



Leave a reply



Submit