как сделать копию unique_ptr с пользовательским удалителем - PullRequest
0 голосов
/ 30 апреля 2018

Я получаю ошибки компиляции, если я делаю копию 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)) ===|

Ответы [ 5 ]

0 голосов
/ 30 апреля 2018

Ошибка, которую вы получаете, заключается в том, что лямбда-выражения не могут быть созданы по умолчанию. Когда вы объявляете p2, вы не передаете аргумент для использования в качестве средства удаления, поэтому компилятор жалуется, что не может создать его по умолчанию.

Этот ответ уже охватывает чистый способ использования пользовательского средства удаления. Но если бы вам пришлось использовать разные средства удаления для объектов одного и того же класса, вам нужно было бы либо передать средство удаления при построении каждого интеллектуального указателя, либо использовать тип, который может быть создан по умолчанию, чтобы 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);
}

Таким образом, вам не только не нужно назначать средство удаления, когда не существует управляемого объекта, но вы также можете определять и использовать другие средства удаления, которые всегда перемещаются вместе с объектом, с которым они созданы.

0 голосов
/ 30 апреля 2018

Здесь есть две ошибки (что подтверждается сообщением об ошибке): вы не можете копировать-назначать лямбду (требуется при построении 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);
}

Обратите внимание, что таким образом будет существовать только один экземпляр вашей лямбды. Чтобы сгенерировать несколько лямбда-экземпляров для нескольких уникальных указателей, вы можете вернуть их из функции, использовать std::function или написать структуру функтора (см. Другие ответы).

0 голосов
/ 30 апреля 2018

Два выпуска.

  1. До C ++ 20 лямбда типы закрытия не являются DefaultConstructible; из-за чего std::unique_ptr использует его как средство удаления, а не DefaultConstructible.

Типы закрытия не являются DefaultConstructible. Типы замыканий имеют конструктор по умолчанию a deleted (until C++14) no (since C++14). (до C ++ 20)

Если захваты не указаны, у типа замыкания есть конструктор по умолчанию по умолчанию. В противном случае у него нет конструктора по умолчанию (это включает случай, когда есть захват по умолчанию, даже если он на самом деле ничего не захватывает). (начиная с C ++ 20)

  1. До C ++ 20 лямбда типы закрытия не являются CopyAssignable; что приводит к тому, что std::unique_ptr использует его в качестве средства удаления, но не CopyAssignable.

Оператор назначения копирования определен как удаленный (и оператор назначения перемещения не объявлен). Типы закрытия не являются CopyAssignable. (до C ++ 20)

Если захваты не указаны, тип закрытия имеет оператор присваивания копии по умолчанию и оператор присваивания перемещения по умолчанию. В противном случае он имеет оператор присваивания удаленной копии (в том числе и случай захвата по умолчанию, даже если он на самом деле ничего не захватывает). (начиная с C ++ 20)

Это означает, что ваш код будет работать с C ++ 20, потому что лямбда-код не определяет захват. До этого вы можете использовать std::function вместо этого; например std::function<void(int*)> del = [](int *p) { cout <<"obj deleted "<<endl;delete p;};

0 голосов
/ 30 апреля 2018

Проблема, связанная с объявлением p2, заключается в том, что тип лямбда не является конструируемым по умолчанию . Поэтому каждому объявлению типа указателя должен быть передан экземпляр средства удаления.

Это можно исправить, заменив лямбда-указатель явной структурой функторов, которая может быть построена по умолчанию. Затем каждый экземпляр типа указателя сможет создать свой собственный экземпляр объекта удаления.

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

EDIT: как сказал @super, проблема со строкой присваивания также заключалась в том, что (до c ++ 20) лямбды также не CopyAssignable . Метод functor, который я разместил выше, исправляет обе проблемы.

0 голосов
/ 30 апреля 2018

вы получаете сообщение об ошибке, потому что в данном случае нет уникального конструктора по умолчанию для 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));
}
...