patterncppMinor
DeepPtr: a deep-copying unique_ptr wrapper in C++
Viewed 0 times
wrapperdeepcopyingdeepptrunique_ptr
Problem
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
Areas of concern are:
```
#pragma once
#include
#include
template
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{ new T{ *other } };
}
}
DeepPtr( DeepPtr&& other )
:myPtr( nullptr )
{
if ( other )
{
myPtr = std::unique_ptr{ 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->(); }
con
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/publicor 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
#include
template
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{ new T{ *other } };
}
}
DeepPtr( DeepPtr&& other )
:myPtr( nullptr )
{
if ( other )
{
myPtr = std::unique_ptr{ 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->(); }
con
Solution
You already accepted Loki's answer (and he made good points) but there is one comment I wanted to add regarding this:
•Any other pitfalls that could be caused by this class.
Your class has no information on the concrete type held within, causing slicing in the presence of derived classes;
Consider this scenario:
The last line will populate ptr2 with a Base instance that has all the sliced values from ptr2's Derived instance (because ptr2's constructorr calls
You have two possible solutions for this:
-
intrusive cloning (your class hierarchy rooted in Base needs to implement a
-
transparent cloning, based on template specialization of a factory function, by the derived type. I needed to implement something similar for the same reason (deep copy of polymorphic pointers). You can see here my implementation.
See the use of
•Any other pitfalls that could be caused by this class.
Your class has no information on the concrete type held within, causing slicing in the presence of derived classes;
Consider this scenario:
class Base { ... }; // not abstract (can be instantiated)
class Derived: public Base { ... };
auto ptr1 = DeepPtr(new Derived{});
auto ptr2 = ptr1;The last line will populate ptr2 with a Base instance that has all the sliced values from ptr2's Derived instance (because ptr2's constructorr calls
new T, which resolves to new Base - not new Derived).You have two possible solutions for this:
-
intrusive cloning (your class hierarchy rooted in Base needs to implement a
virtual Base* clone() = 0 where each specialization of Base returns it's own instance); this cloning implementation would be then used for deep copy;-
transparent cloning, based on template specialization of a factory function, by the derived type. I needed to implement something similar for the same reason (deep copy of polymorphic pointers). You can see here my implementation.
See the use of
to_polymorphic functions in my question, and how they instantiate the correct cloning function, transparently.Code Snippets
class Base { ... }; // not abstract (can be instantiated)
class Derived: public Base { ... };
auto ptr1 = DeepPtr<Base>(new Derived{});
auto ptr2 = ptr1;Context
StackExchange Code Review Q#103744, answer score: 6
Revisions (0)
No revisions yet.