使用自定义删除器的std::shared_ptr的typedef别名

4

我希望为std::shared_ptr创建一个自定义删除器的别名。

这段代码可以工作,但只适用于独占式指针。对于标记为[1]的行,我会收到关于无效模板参数数量的错误信息。

我注意到std::unique_ptrstd::shared_ptr的模板和构造函数参数不同,如此处所列(参见此处)(参见此处)

我注意到这个问题可能是这个的重复,但我无法解决我的问题

#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>();
    }
}

对于 [ 2 ]
我该如何告诉它使用我的删除器?

对于 [ 3 ] 如果 [ 2 ] 不可行,那么我应该如何创建一个能够为我创建 my_shared_ptr<T> 的函数?

错误信息对于 [ 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;
           ^~~~~~~~~~

[ 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)
                                       ^

编辑 添加了my_make_shared函数, 现在[4]已经可以编译通过了。

编辑 我注意到(通过观察错误)我的别名shared_ptr<MyClass>并不是真正的shared_ptr<MyClass>,而是shared_ptr<unique_ptr<MyClass>>的别名 - 它试图创建一个指向指针的指针(一开始我以为它只是重定向构造函数)

编辑 将指向指针的别名注释掉。使用[1]和[3]的想法确实是毫无意义的甚至是没有指针的

添加新的(正确的)shared_ptr别名

编辑

整个代码现在都可以工作了。所有问题都解决了。

编辑 最后,有个小问题:
为什么我不能把return my_unique_ptr<T>(new T(std::forward<Args>(args)...));改成return {new T(std::forward<Args>(args)...)};

我得到了这个错误:

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)...)};
                                                               ^

了解到std::unique_ptr的构造函数是明确的(explicit),但std::shared_ptr的构造函数不是。

非常感谢SO提供的帮助!


1
(1) 请发布错误信息。 (2) 看起来你在[1]中忘记了一个闭合括号>。 (3) 我没有看到任何typedef - Rakete1111
2
shared_ptr 实际上是类型擦除其删除器,而不是模板参数。也许可以看一下复制 make_shared 的接口。 - BoBTFish
@Rakete1111已经修复,但是它仍然无法编译。我已经粘贴了错误信息。 - Xeverous
1
您上次的编辑有误:对于同时接受单个指针的智能指针,构造函数应为“显式”。 - Jarod42
谢谢,刚刚注意到 Deleter<T>{} 可以被 Deleter<T>() 替代 - 哪个更符合惯例? - Xeverous
1
避免最令人烦恼的解析,禁止缩小参数范围。()适用于C++11之前,看起来像函数调用(因此在某些情况下可以被函数或宏替代)。 - Jarod42
1个回答

3
你可以执行一个类似 my_make_shared 的操作,示例如下:
template <class T, class... Args>
std::shared_ptr<T> my_make_shared(Args&&... args)
{
    return {new T(std::forward<Args>(args)...), Deleter<T>{}};
}

使用方法:

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

而对于[3],应该是这样的:
 std::shared_ptr<MyClass> p(my_make_unique<MyClass>());

请注意,std::shared_ptr<std::unique_ptr<T/*, D*/>> 在大多数情况下是无意义的。

请添加一个创建对象的示例代码,我无法同时使用[2]和[3]使其正常工作。 - Xeverous
@Xeverous:已添加 Ex。 - Jarod42
你能解释一下我上一个问题中的问题吗? - Xeverous
1
只接受指针的函数也是“explicit”的,但接收“std::unique_ptr&&”的函数不是“explicit”的,而且是安全的(所有权转移)。 - Jarod42
啊,确实;我的 my_make_ 函数正在调用不同的重载。 - Xeverous
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接