I have a base class with a pointer member. I would have to make an educated guess to determine whether it should be an unique_ptr
or a shared_ptr
. None of them seems to solve my particular use case.
class Base
{
public:
Base(): pInt(std::unique_ptr<int>(new int(10))) {};
virtual std::unique_ptr<int> get() = 0;
//Base(): pInt(std::shared_ptr<int>(new int(10))) {}; // Alternate implementation
//virtual std::shared_ptr<int> get() = 0; // Alternate implementation
private:
std::unique_ptr<int> pInt;
//std::shared_ptr<int> pInt; // Alternate implementation
};
The base class has been derived onto Derived1
and Derived2
. The former returns the unique_ptr
member pInt
where the later returns a local unique_ptr
object.
class Derived1: public Base
{
public:
Derived1() {};
virtual std::unique_ptr<int> get()
{
//return std::move(pInt); Will compile but the ownership is lost
return pInt;
}
private:
std::unique_ptr<int> pInt;
};
class Derived2: public Base
{
public:
Derived2() {};
virtual std::unique_ptr<int> get()
{
std::unique_ptr<int> pInt(new int());
return pInt;
}
private:
std::unique_ptr<int> pInt;
};
Derived1
's implementation of get would not implicitly transfer the ownership as the member pointer variable is not an eXpiring value, where as the Derived2
's implementation can. This behaviour is well documented in the standard
see 12.8 §34 and §35:
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object [...] This elision of copy/move operations, called copy elision, is permitted [...] in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object with the same cv-unqualified type as the function return type [...]
When the criteria for elision of a copy operation are met and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.
Nevertheless, if I explicitly transfer the ownership via the std::move
, the member pointer would be unusable in the future.
Alternatively, I would have to make the definition of the pointer as shared_ptr
but that would be an extra overhead for the implementation of Derived2::get
.
Note It should be considered that the occurrence of Derived2::get
is more compared to Derived1::get
so the design decision of using std:: shared_ptr
can have a considerable relative impact.