std::deep_ptr aka deep copying smart pointer has not yet been introduced to the STL though several C++ experts have proposed an implementation in the past. See n3339.pdf for one such proposal and a list of possible implementations.
In this post I want to share my implementation of deep copying pointer I recently came up with. Its interface is virtually identical to std::unique_ptr with the exception of additional cloner template parameter (a policy class) that specifies how deep copies are to be made. By default the pointer is copied by doing return (p ? new T{ *p } : nullptr); that is by invoking the copy constructor of T.
My example program illustrates how to create a cloning policy for polymorphic types; in other words how to make deep copies when deep_ptr<Base> actually points to some struct Derived : Base and types in a given inheritance hierarchy implement virtual S* clone():
1 2 3 4 5 |
auto clone = [](S* p) { return p->clone(); }; using clone_ptr = deep_ptr<S, decltype(clone)>; auto p1 = clone_ptr(new S, clone); auto p2 = p1; |
Finally, and just like the std::unique_ptr, my pointer type can be used with a custom deleter:
1 2 3 4 |
auto del = [](S* p) { delete p; }; using del_ptr = deep_ptr<S, decltype(clone), decltype(del)>; auto p = del_ptr(new S, clone, del); |
del will be called on a pointer managed by p when it goes out of scope or when p is assigned to.
Example program on my GitHub account: deep_ptr.cpp. The implementation: deep_ptr.hpp.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
#pragma once #include <memory> #include <utility> #include <compare> #include <type_traits> #include <initializer_list> #include <cstddef> template<typename T> struct default_cloner { using pointer = T*; pointer operator () (pointer p) const { return (p ? new T{ *p } : nullptr); } }; template<typename T, typename C = default_cloner<T>, typename D = std::default_delete<T>> class deep_ptr { public: using pointer = T*; using element_type = T; using reference_type = T&; using cloner_type = C; using deleter_type = D; constexpr deep_ptr() noexcept = default; constexpr deep_ptr(std::nullptr_t) noexcept {} explicit deep_ptr(pointer p) noexcept : m_p{ p } {} deep_ptr(pointer p, cloner_type c) : m_c{ c }, m_p{ p } {} deep_ptr(pointer p, deleter_type d) : m_d{ d }, m_p{ p } {} deep_ptr(pointer p, cloner_type c, deleter_type d) : m_c{ c }, m_d{ d }, m_p{ p } {} deep_ptr(const deep_ptr& d) : m_c{ d.m_c }, m_d{ d.m_d }, m_p{ get_cloner()(d.m_p) } {} deep_ptr(deep_ptr&& d) noexcept : m_c{ std::move(d.m_c) }, m_d{ std::move(d.m_d) }, m_p{ std::exchange(d.m_p, nullptr) } {} template<typename U, typename V, typename W> deep_ptr(const deep_ptr<U, V, W>& d) : m_c{ d.m_c }, m_d{ d.m_d }, m_p{ get_cloner()(d.m_p) } {} template<typename U, typename V, typename W> deep_ptr(deep_ptr<U, V, W>&& d) noexcept : m_c{ std::move(d.m_c) }, m_d{ std::move(d.m_d) }, m_p{ std::exchange(d.m_p, nullptr) } {} ~deep_ptr() noexcept { get_deleter()(get()); } deep_ptr& operator = (deep_ptr r) noexcept { swap(r); return *this; } template<typename U, typename V, typename W> friend bool operator == (const deep_ptr<T, C, D>& x, const deep_ptr<U, V, W>& y) noexcept { return x.m_p == y.m_p; } template<typename U, typename V, typename W> friend auto operator <=> (const deep_ptr<T, D, D>& x, const deep_ptr<U, V, W>& y) noexcept { return x.m_p <=> y.m_p; } explicit operator bool () const noexcept { return m_p != nullptr; } reference_type operator * () const { return *m_p; } pointer operator -> () const noexcept { return m_p; } pointer get() const noexcept { return m_p; } deleter_type& get_deleter() noexcept { return m_d; } const deleter_type& get_deleter() const noexcept { return m_d; } cloner_type& get_cloner() noexcept { return m_c; } const cloner_type& get_cloner() const noexcept { return m_c; } pointer release() noexcept { return std::exchange(m_p, nullptr); } void reset(pointer p = pointer()) noexcept { get_deleter()(std::exchange(m_p, p)); } void swap(deep_ptr& o) noexcept { std::swap(m_c, o.m_c); std::swap(m_d, o.m_d); std::swap(m_p, o.m_p); } private: template<typename, typename, typename> friend class deep_ptr; cloner_type m_c = cloner_type(); deleter_type m_d = deleter_type(); pointer m_p = pointer(); }; template<typename T, typename C, typename D> inline void swap(deep_ptr<T, C, D>& x, deep_ptr<T, C, D>& y) { x.swap(y); } template<typename T, typename V> inline auto make_deep(std::initializer_list<V> l) { using U = std::decay_t<T>; return deep_ptr<T>(new U(l)); } template<typename T, typename... A> inline auto make_deep(A&&... a) { return deep_ptr<T>(new T(std::forward<A>(a)...)); } |