HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

DeepPtr: a deep-copying unique_ptr wrapper in C++

Submitted by: @import:stackexchange-codereview··
0
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 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
#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:

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.