When using the pimple idiom it is necessary to represent your "impl" as a forward declared pointer in the header file. This necessitates a lot of boilerplate code to implement the rule of five.
Instead, I want to wrap std::unique_ptr
with a template class DeepPtr
which automatically deep copies on copy construction, assignment, and "deep" swaps on move construction and move assignment. The resulting class could also be convenient for "value-like" objects that need to be passed by handle due to polymorphism.
Areas of concern are:
- Correct use of the swap idiom (should the local swap function be
private
/public
or done some other way)? - Const correctness of the dereferencing operator overloads
- Signature of the assignment operator function (
const DeepPtr& other
) which uses this const ref only to copy to temp. This seems strange, but correct. - Any other pitfalls that could be caused by this class.
#pragma once
#include <iostream>
#include <memory>
template <typename T>
class DeepPtr
{
public:
DeepPtr() :myPtr( nullptr ) {}
DeepPtr( const T& value ) :myPtr( new T{ value } ) {}
DeepPtr( const DeepPtr& other )
:myPtr( nullptr )
{
if ( other )
{
myPtr = std::unique_ptr<T>{ new T{ *other } };
}
}
DeepPtr( DeepPtr&& other )
:myPtr( nullptr )
{
if ( other )
{
myPtr = std::unique_ptr<T>{ new T{ *other } };
}
}
DeepPtr& operator=( const DeepPtr& other )
{
DeepPtr temp{ other };
swap( *this, temp );
return *this;
}
DeepPtr& operator=( DeepPtr&& other )
{
swap( *this, other );
return *this;
}
static void swap( DeepPtr& left, DeepPtr& right ) { std::swap( left.myPtr, right.myPtr ); }
T& operator*() { return *myPtr; }
const T& operator*() const { return *myPtr; }
T* const operator->() { return myPtr.operator->(); }
const T* const operator->() const { return myPtr.operator->(); }
const T* const get() const { return myPtr.get(); }
operator bool() const { return (bool)myPtr; }
private:
std::unique_ptr<T> myPtr;
};
{ }
to initialize yournew
ed object. This will call the initializer list constructor, if and when it is available. It means that if you haveT=std::container
this will (may?) behave differently than if T is a POD type, or something that does not define an initializer list constructor. \$\endgroup\$