Rule A8-4-12 (required, design, automated)
A std::unique_ptr shall be passed to a function as: (1) a copy to express the function assumes ownership (2) an lvalue reference to express that the function replaces the managed object.
Rationale
Transferring ownership in the (1) case is unconditional. A temporary std::unique_ptr is constructed implicitly and move-initialized from the caller’s std::unique_ptr and then passed to the function. This guarantees that the caller’s std::unique_ptr object is empty. Passing an lvalue reference is suggested to be used if a called function is supposed to replace the object managed by the passed std::unique_ptr, e.g. call assignment operator or reset method. Otherwise, it is recommended to pass an lvalue reference to the underlying object instead, see A8-4-11, A8-4-10. Note: Passing a const lvalue reference to std::unique_ptr does not take ownership and does not allow to replace the managed object. Also, the const qualifier does not apply to the underlying object, but to the smart pointer itself. It is suggested to pass a const lvalue reference to the underlying object instead, see A8-4-11, A8-4-10.
Exception
It is allowed to transfer ownership by passing a std::unique_ptr by an rvalue reference in case this reference is moved into a std::unique_ptr object inside the called function.
Example
// $Id: A8-4-12.cpp 308795 2018-02-23 09:27:03Z michal.szczepankiewicz $
#include <memory>
#include <iostream>
//compliant, transfers an ownership
void Value(std::unique_ptr<int> v) { }
//compliant, replaces the managed object
void Lv1(std::unique_ptr<int>& v)
{
v.reset();
}
//non-compliant, does not replace the managed object
void Lv2(std::unique_ptr<int>& v) {}
//compliant by exception
void Rv1(std::unique_ptr<int>&& r)
{
std::unique_ptr<int> v(std::move(r));
}
//non-compliant
void Rv2(std::unique_ptr<int>&& r) {}
int main(void)
{
auto sp = std::make_unique<int>(7);
Value(std::move(sp));
//sp is empty
auto sp2 = std::make_unique<int>(9);
Rv1(std::move(sp2));
//sp2 is empty, because it was moved from in Rv1 function
auto sp3 = std::make_unique<int>(9);
Rv2(std::move(sp3));
//sp3 is not empty, because it was not moved from in Rv1 function
return 0;
}
See also
HIC++ v4.0 [9]: 8.2.4: Do not pass std::unique_ptr by const reference.
C++ Core Guidelines [11]: R.32: Take a unique_ptr