Passing std::unique_ptr as function argument

Question | Mar 3, 2020 | nextptr 

Basics

An std::unique_ptr is a smart pointer that exclusively owns and manages an object (or an array of objects) through a pointer. The exclusive ownership of an object requires that the managing unique_ptr cannot be copied to avoid the shared possession of the object at any point. However, a unique_ptr can be moved to another unique_ptr:

auto up = std::make_unique<std::string>("A string");

auto cup = up; //!ERROR. Can not copy.

auto mup = std::move(up); //OK

A unique_ptr is made non-copiable via deleted copy constructor and copy assignment operator. The copy constructor and the copy assignment operator in a typical implementation of unique_ptr are implicitly deleted. In a typical implementation of unique_ptr, the definitions of move constructor and move assignment operator cause implicit deletion of the copy constructor and the copy assignment operator because they are not explicitly defined.

The non-copiability affects how a unique_ptr is passed to a function. Passing a unique_ptr to a function that takes a unique_ptr by reference is a no-brainer because there is no copy (or move) involved:

void bar(std::unique_ptr<std::string>& refP) {
 // ...
}

//call bar()
auto up = std::make_unique<std::string>("A large string");

bar(up); //OK. Passed by reference

However, a unique_ptr has to be moved while passing it to a function that takes a unique_ptr by value. The move could be explicit for an lvalue argument or implicit for a temporary, as shown below:

void foo(std::unique_ptr<std::string> cp) {
 //...
}

//call foo

auto up = std::make_unique<std::string>("A large string");

foo(up); //!ERROR. No copy allowed.

foo(std::move(up)); //OK. Explicit move

foo(std::make_unique<std::string>("A large string")); //OK. Implicit move

A Question

Consider a function, func, that takes a unique_ptr by value:

void func(std::unique_ptr<std::string> cp) {
 //...
}

Below are some use cases of calling func; one of them fails to compile:

A.

void callerA(const char* str) {
 //...
 auto up = std::make_unique<std::string>(str);

 func(up.release());
 //...
}

B.

void callerB(std::string* rawP) {
 //...
 func(std::unique_ptr<std::string>(rawP));
 //...
}

C.

void callerC(std::unique_ptr<std::string>& refP) {
 //...
 func(std::move(refP));
 //...
}

D.

void callerD(const std::string& refS) {
 //...
 auto up = std::make_unique<std::string>(refS);
 //...
 func(std::move(up));
 //...
}

Select below the scenario from above that has an invalid call to func (fails to compile). Check Explanations for details: