如何使用自定义删除器复制 unique_ptr

how to make copy of unique_ptr wih custom deleter

如果我使用自定义删除器制作 unique_ptr 的副本,我会遇到编译错误。请有人帮助我。

#include <iostream>
#include<memory>
#include <algorithm>
using namespace std;

auto del = [](int *p) { cout <<"obj deleted "<<endl;delete p;};
int main()
{
   unique_ptr<int, decltype(del)> p1(new int(10), del);
   unique_ptr<int,decltype(del)> p2;
   p2 = std::move(p1);
}

错误:

C:\Program Files (x86)\CodeBlocks\MinGW\lib\gcc\mingw32.1.0\include\c++\tuple||In instantiation of 'constexpr std::_Head_base<_Idx, _Head, true>::_Head_base() [with unsigned int _Idx = 1u; _Head = <lambda(int*)>]':|
C:\Program Files (x86)\CodeBlocks\MinGW\lib\gcc\mingw32.1.0\include\c++\tuple|353|required from 'constexpr std::_Tuple_impl<_Idx, _Head>::_Tuple_impl() [with unsigned int _Idx = 1u; _Head = <lambda(int*)>]'|
C:\Program Files (x86)\CodeBlocks\MinGW\lib\gcc\mingw32.1.0\include\c++\tuple|202|required from 'constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl() [with unsigned int _Idx = 0u; _Head = int*; _Tail = {<lambda(int*)>}]'|
C:\Program Files (x86)\CodeBlocks\MinGW\lib\gcc\mingw32.1.0\include\c++\tuple|602|required from 'constexpr std::tuple<_T1, _T2>::tuple() [with _T1 = int*; _T2 = <lambda(int*)>]'|
C:\Program Files (x86)\CodeBlocks\MinGW\lib\gcc\mingw32.1.0\include\c++\bits\unique_ptr.h|158|required from 'constexpr std::unique_ptr<_Tp, _Dp>::unique_ptr() [with _Tp = int; _Dp = <lambda(int*)>]'|
F:d\C++CodeProject\Hello\main.cpp|10|required from here|
C:\Program Files (x86)\CodeBlocks\MinGW\lib\gcc\mingw32.1.0\include\c++\tuple|59|error: use of deleted function '<lambda(int*)>::<lambda>()'|
F:d\C++CodeProject\Hello\main.cpp|6|note: a lambda closure type has a deleted default constructor|
C:\Program Files (x86)\CodeBlocks\MinGW\lib\gcc\mingw32.1.0\include\c++\bits\unique_ptr.h||In instantiation of 'std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(std::unique_ptr<_Tp, _Dp>&&) [with _Tp = int; _Dp = <lambda(int*)>]':|
F:d\C++CodeProject\Hello\main.cpp|11|required from here|
C:\Program Files (x86)\CodeBlocks\MinGW\lib\gcc\mingw32.1.0\include\c++\bits\unique_ptr.h|252|error: use of deleted function '<lambda(int*)>&<lambda(int*)>::operator=(const<lambda(int*)>&)'|
F:d\C++CodeProject\Hello\main.cpp|6|note: a lambda closure type has a deleted copy assignment operator|
||=== Build failed: 2 error(s), 8 warning(s) (0 minute(s), 1 second(s)) ===|

您收到一条错误消息,因为在这种情况下 unique_ptr 上没有默认构造函数,因此它们总是被初始化

但你可以做到

auto del = [](int *p) { cout <<"obj deleted "<<endl;delete p;};
int main()
{
   unique_ptr<int, decltype(del)> p1(new int(10), del);
   unique_ptr<int,decltype(del)> p2(std::move(p1));
}

两个问题。

  1. 在 C++20 之前 lambda 闭包类型不是 DefaultConstructible;这导致 std::unique_ptr 将其用作删除器而不是 DefaultConstructible。

Closure types are not DefaultConstructible. Closure types have a deleted (until C++14) no (since C++14) default constructor. (until C++20)

If no captures are specified, the closure type has a defaulted default constructor. Otherwise, it has no default constructor (this includes the case when there is a capture-default, even if it does not actually capture anything). (since C++20)

  1. C++20 之前的 lambda 闭包类型不可复制;这导致 std::unique_ptr 也将其用作删除器而不是 CopyAssignable。

The copy assignment operator is defined as deleted (and the move assignment operator is not declared). Closure types are not CopyAssignable. (until C++20)

If no captures are specified, the closure type has a defaulted copy assignment operator and a defaulted move assignment operator. Otherwise, it has a deleted copy assignment operator (this includes the case when there is a capture-default, even if it does not actually capture anything). (since C++20)

这意味着,您的代码自 C++20 起即可运行,因为 lambda 未指定捕获。在此之前,您可以使用 std::function 代替;例如std::function<void(int*)> del = [](int *p) { cout <<"obj deleted "<<endl;delete p;};

您在声明 p2 时遇到的问题是 lambda 的类型不是 default constructible。因此,指针类型的每个声明都需要将删除器的实例传递给它。

您可以通过将 deleter lambda 替换为默认可构造的显式仿函数结构来解决此问题。然后,指针类型的每个实例都将能够创建自己的删除器对象实例。

struct MyDeleter
{
    void operator()(int* p){cout << "deleted" << endl; delete p;}
};

int main()
{
    //don't need to specify an instance of the deleter since it is default_constructible.
    unique_ptr<int, MyDeleter> p1(new int(10)); 
    unique_ptr<int, MyDeleter> p2;
    p2 = std::move(p1);
}

编辑: 正如@super 所说,分配行的问题还在于(pre-c++20)lambda 也不是 CopyAssignable。我在上面发布的仿函数方法解决了这两个问题。

这里有两个错误(如错误消息所示):您不能复制分配 lambda(在 p1 的构造中需要),并且您不能默认构造 lambda(在 p1 的构造中需要) p2).

的默认初始化

这修复了这两个错误:

auto del = [](int *p) { cout <<"obj deleted "<<endl;delete p;};
int main()
{
   unique_ptr<int, decltype(del)> p1(new int(10), std::move(del));
   unique_ptr<int, decltype(del)> p2 = std::move(p1);
}

但是请注意,只有一个 lambda 实例会以这种方式存在。要为多个唯一指针生成多个 lambda 实例,您可以 return 从函数中使用它,使用 std::function 或编写仿函数结构(参见其他答案)。

您收到的错误是因为 lambda 不可默认构造。当您声明 p2 时,您没有传递用作删除器的参数,因此编译器会抱怨无法默认构造它。

答案已经涵盖了使用自定义删除器的简洁方法。但是如果你必须对相同 class 的对象使用不同的删除器,你要么必须在构造每个智能指针时传递一个删除器,要么使用可默认构造的类型,以便 nullptr 可以有一个 "null" 删除器对象。

auto del = [](int *p) { cout << "obj deleted " << endl; delete p; };
int main() {
    unique_ptr<int, std::function<void(int*)>> p1(new int(10), del);
    unique_ptr<int, std::function<void(int*)>> p2;
    p2 = std::move(p1);
}

这样,当没有管理对象时,您不仅不必分配删除器,而且还可以定义和使用其他删除器,这些删除器始终随创建它们的对象一起移动。