Reputation: 4337
Let assume I have a DataProcessor template class that holds a smart pointer to the processed data that has operator * and operator ->:
template <class DataPointer>
class DataProcessor
{
public:
//it is not clear how to implement the constructor
//DataProcessor(DataPointer p) : pData(p) {};
void Process() { /* do something with *pData */ };
private:
DataPointer pData;
}
How to implement the constructor to make DataProcessor work with both std::unique_ptr (the constructor should accept it by && and move it) and std::shared_ptr (the constructor should accept it by & and copy it)? Is it possible to have some kind of a uniform constructor?
Actually I have a class that holds a smart Win32 handle UniqueHandle and SharedHandle that have the similar semantics like std::unique_ptr and std::shared_ptr. So it is a general question on how to implement a scenario like this.
Upvotes: 0
Views: 488
Reputation: 473996
Your choices are essentially these:
Take the parameter by value:
DataProcessor(DataPointer p) : pData(std::move(p)) {}
If DataPointer
is move-only, then the user will have to call it through std::move
, which will move-construct p
, which is then used to move-construct pData
. If it is copyable, then p
will be copy/move constructed based on how the user passes the value. From there, it will move construct pData
.
Note that this version adds an additional move operation.
Take the parameter by rvalue reference, always:
DataProcessor(DataPointer &&p) : pData(std::move(p)) {}
In this case, if DataPointer
is not move-only, and the user wants to pass an lvalue, the user must explicitly copy the value into a temporary used to initialize p
. That would look something like this:
DataProcessor<shared_ptr<T>> dp(shared_ptr{sp});
Where sp
is an existing shared_ptr
that you want to copy from. This does only one move when given an object to move from, but does a copy+move when copying.
Write two functions, employing SFINAE to remove the copying version if DataPointer
is non-copyable. This version has the advantage of doing no additional moves:
DataProcessor(DataPointer &&p) : pData(std::move(p)) {}
template<typename T = DataPointer>
DataProcessor(std::enable_if_t<std::is_copy_constructible_v<T>, const T&> p)
: pData(p) {}
Upvotes: 2