【问题标题】:how to make copy of unique_ptr wih custom deleter如何使用自定义删除器制作 unique_ptr 的副本
【发布时间】:2018-10-10 15:41:37
【问题描述】:

如果我使用自定义删除器制作 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\5.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\5.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\5.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\5.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\5.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:\3d\C++CodeProject\Hello\main.cpp|10|required from here|
C:\Program Files (x86)\CodeBlocks\MinGW\lib\gcc\mingw32\5.1.0\include\c++\tuple|59|error: use of deleted function '<lambda(int*)>::<lambda>()'|
F:\3d\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\5.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:\3d\C++CodeProject\Hello\main.cpp|11|required from here|
C:\Program Files (x86)\CodeBlocks\MinGW\lib\gcc\mingw32\5.1.0\include\c++\bits\unique_ptr.h|252|error: use of deleted function '<lambda(int*)>&<lambda(int*)>::operator=(const<lambda(int*)>&)'|
F:\3d\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_ptrs。

标签: c++ c++11 lambda unique-ptr


【解决方案1】:

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

您可以通过将删除器 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 所说,赋值行的问题还在于(c++20 之前)lambda 也不是CopyAssignable。我在上面发布的仿函数方法解决了这两个问题。

【讨论】:

  • 问题在于 lambda 不是 CopyAssignable。
  • 你错过了终止"
  • 另一种解决方案是使用std::function
  • @super 你说得对!我主要关心 unique_ptr 声明的问题,但我发布的代码解决了这两个问题。
  • @super 有两个不同的问题。
【解决方案2】:

两个问题。

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

闭包类型不是 DefaultConstructible。闭包类型有 a deleted (until C++14) no (since C++14) 默认构造函数。 (直到 C++20)

如果未指定捕获,则闭包类型具有默认的默认构造函数。否则,它没有默认构造函数(这包括存在默认捕获的情况,即使它实际上没有捕获任何东西)。 (C++20 起)

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

复制赋值运算符定义为已删除(并且未声明移动赋值运算符)。闭包类型不是 CopyAssignable。 (直到 C++20)

如果未指定捕获,则闭包类型具有默认的复制赋值运算符和默认的移动赋值运算符。否则,它有一个已删除的复制赋值运算符(这包括存在捕获默认值的情况,即使它实际上没有捕获任何东西)。 (C++20 起)

这意味着,您的代码将从 C++20 开始工作,因为 lambda 不指定捕获。在此之前,您可以改用std::function;例如std::function&lt;void(int*)&gt; del = [](int *p) { cout &lt;&lt;"obj deleted "&lt;&lt;endl;delete p;};

【讨论】:

【解决方案3】:

您收到一条错误消息,因为在这种情况下,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));
}

【讨论】:

  • 这是不正确的。 unique_ptr 可以默认初始化为 null。
  • 嗯,它是半正确的。普通unique_ptrs 可以默认初始化,但不能默认初始化那些以 lambda 作为删除器的(因为 lamdba 不能默认构造)。正确的错误,错误的原因。
【解决方案4】:

这里有两个错误(如错误消息所示):您无法复制分配 lambda(在构造 p1 时需要),并且您无法默认构造 lambda(在默认初始化时需要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 实例,您可以从函数中返回它,使用 std::function 或编写函子结构(参见其他答案)。

【讨论】:

    【解决方案5】:

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

    This 答案已经涵盖了使用自定义删除器的干净方法。但是如果你必须对同一个类的对象使用不同的删除器,你要么必须在构造每个智能指针时传递一个删除器,要么使用默认可构造的类型,以便 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);
    }
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-13
      • 2013-03-30
      • 2017-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-11
      • 2016-11-23
      相关资源
      最近更新 更多