Xeverous
Xeverous

Reputation: 1014

Typedef alias of std::shared_ptr with custom deleter

I want to make an alias for std::shared_ptr with custom deleter.

This code works, but only for unique pointer. I get error about invalid amount of template arguments for line marked with [ 1 ].

I have noticed that template and ctor arguments for std::unique_ptr and std::shared_ptr differ as listed here and here

I have noticed that this question might be a duplicate of this, but I can't figure out how to solve my problem

#include <memory>
#include <iostream>

template<class T>
struct Deleter {
    void operator()(T* p) const noexcept {
       p->Drop(); // SFINAE
    };
};

template <class T>
using my_unique_ptr = std::unique_ptr<T, Deleter<T>>;

//template <class T>
//using my_shared_ptr = std::shared_ptr<T, Deleter<T>>; // [1] does not work
//using my_shared_ptr = std::shared_ptr<my_unique_ptr<T>>; // this is pointless

template <class T>
using my_shared_ptr = std::shared_ptr<T>;


template <class T, class... Args>
my_unique_ptr<T> my_make_unique(Args&&... args)
{
    return my_unique_ptr<T>(new T(std::forward<Args>(args)...));
}

template <class T, class... Args>
std::shared_ptr<T> my_make_shared(Args&&... args)
{
    return {new T(std::forward<Args>(args)...), Deleter<T>{}};
//  return {new T(std::forward<Args>(args)...), Deleter<T>()}; this also works
}

class MyClass{
    public:
      MyClass() 
      {
          std::cout << "Default ctor\n";
      }
      ~MyClass()
      {
          std::cout << "Default dtor\n";
      }
      void Drop()
      {
          std::cout << "Custom deleter\n";
          delete this;
      }
};

int main()
{
    {
        my_unique_ptr<MyClass> p1(new MyClass);
        my_unique_ptr<MyClass> p2 = my_make_unique<MyClass>();
    }

    {
//      my_shared_ptr<MyClass> p(new MyClass) // [2] does not work
//      my_shared_ptr<MyClass> p(my_make_unique<MyClass>()); // [3] does not work
        std::shared_ptr<MyClass> p1 = my_make_shared<MyClass>(); // [4] works
        my_shared_ptr<MyClass> p2 = my_make_shared<MyClass>();
    }
}

For [ 2 ]
How do I make it know to use my deleter?

For [ 3 ] If [ 2 ] isn't possible, then how do I create a function than can create a my_shared_ptr<T> for me?

Error for [ 1 ]

main.cpp:15:51: error: wrong number of template arguments (2, should be 1)
 using my_shared_ptr = std::shared_ptr<T, Deleter<T>> // does not work
                                                   ^~
In file included from /usr/local/include/c++/6.3.0/bits/shared_ptr.h:52:0,
                 from /usr/local/include/c++/6.3.0/memory:82,
                 from main.cpp:1:
/usr/local/include/c++/6.3.0/bits/shared_ptr_base.h:343:11: note: provided for 'template<class _Tp> class std::shared_ptr'
     class shared_ptr;
           ^~~~~~~~~~

Error for [ 2 ]

In file included from /usr/local/include/c++/6.3.0/bits/shared_ptr.h:52:0,
                 from /usr/local/include/c++/6.3.0/memory:82,
                 from main.cpp:1:
/usr/local/include/c++/6.3.0/bits/shared_ptr_base.h: In instantiation of 'std::__shared_ptr<_Tp, _Lp>::__shared_ptr(_Tp1*) [with _Tp1 = MyClass; _Tp = std::unique_ptr<MyClass, Deleter<MyClass> > __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]':
/usr/local/include/c++/6.3.0/bits/shared_ptr.h:117:32:   required from 'std::shared_ptr<_Tp>::shared_ptr(_Tp1*) [with _Tp1 = MyClass; _Tp = std::unique_ptr<MyClass, Deleter<MyClass> >]'
main.cpp:48:45:   required from here
/usr/local/include/c++/6.3.0/bits/shared_ptr_base.h:885:39: error: cannot convert 'MyClass*' to 'std::unique_ptr<MyClass, Deleter<MyClass> >*' in initialization
         : _M_ptr(__p), _M_refcount(__p)
                                       ^

Edit Added my_make_shared function, now [4] compiles fine.

Edit I have noticed (by observing errors) that my alias for shared_ptr<MyClass> is not really an alias for shared_ptr<MyClass>, but an alias for shared_ptr<unique_ptr<MyClass>> - it's trying to create a pointer to pointer (fisrt I thought it's just redirecting constructor)

Edit Commented out alias for pointer to pointer. The idea of using [ 1 ] and [ 3 ] is indeed pointless or maybe even pointerless.

Added new (correct) alias for shared_ptr

Edit

The whole code works now. All problems solved.

Edit Last, minor question:
Why I can't change return my_unique_ptr<T>(new T(std::forward<Args>(args)...)); to return {new T(std::forward<Args>(args)...)};?

I'm getting this error:

main.cpp: In instantiation of 'my_unique_ptr<T> my_make_unique(Args&& ...) [with T = MyClass; Args = {}; my_unique_ptr<T> = std::unique_ptr<MyClass, Deleter<MyClass> >]':
main.cpp:56:61:   required from here
main.cpp:26:63: error: converting to 'my_unique_ptr<MyClass> {aka std::unique_ptr<MyClass, Deleter<MyClass> >}' from initializer list would use explicit constructor 'std::unique_ptr<_Tp, _Dp>::unique_ptr(std::unique_ptr<_Tp, _Dp>::pointer) [with _Tp = MyClass; _Dp = Deleter<MyClass> std::unique_ptr<_Tp, _Dp>::pointer = MyClass*]'
                     return {new T(std::forward<Args>(args)...)};
                                                               ^

Understood that ctor of std::unique_ptr is explicit, but ctor of std::shared_ptr is not.

Thanks SO for all your help!

Upvotes: 4

Views: 899

Answers (1)

Jarod42
Jarod42

Reputation: 217293

You may do a my_make_shared, something like:

template <class T, class... Args>
std::shared_ptr<T> my_make_shared(Args&&... args)
{
    return {new T(std::forward<Args>(args)...), Deleter<T>{}};
}

Usage:

 std::shared_ptr<MyClass> p(my_make_shared<MyClass>());

And for [3], it should be:

 std::shared_ptr<MyClass> p(my_make_unique<MyClass>());

Note that std::shared_ptr<std::unique_ptr<T/*, D*/>> is mostly pointless.

Upvotes: 3

Related Questions