Хранение unique_ptr с пользовательским удалением на карте
Почему это не работает?
#include <map>
#include <memory>
void deleter(int* i) {
delete i;
}
std::map<int, std::unique_ptr<int, decltype(&deleter)>> m;
void foo(int* i) {
m[0] = std::unique_ptr<int, decltype(&deleter)>(i, &deleter);
}
Проверьте непонятную ошибку компиляции https://godbolt.org/z/Uhp9NO.
In file included from <source>:1:
In file included from /opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/map:61:
In file included from /opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/bits/stl_map.h:63:
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/tuple:1668:9: error: no matching constructor for initialization of 'std::unique_ptr<int, void (*)(int *)>'
second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/tuple:1655:9: note: in instantiation of function template specialization 'std::pair<const int, std::unique_ptr<int, void (*)(int *)> >::pair<int &&, 0>' requested here
: pair(__first, __second,
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/ext/new_allocator.h:136:23: note: in instantiation of function template specialization 'std::pair<const int, std::unique_ptr<int, void (*)(int *)> >::pair<int &&>' requested here
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/bits/alloc_traits.h:475:8: note: in instantiation of function template specialization '__gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<const int, std::unique_ptr<int, void (*)(int *)> > > >::construct<std::pair<const int, std::unique_ptr<int, void (*)(int *)> >, const std::piecewise_construct_t &, std::tuple<int &&>, std::tuple<> >' requested here
{ __a.construct(__p, std::forward<_Args>(__args)...); }
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/bits/stl_tree.h:637:23: note: in instantiation of function template specialization 'std::allocator_traits<std::allocator<std::_Rb_tree_node<std::pair<const int, std::unique_ptr<int, void (*)(int *)> > > > >::construct<std::pair<const int, std::unique_ptr<int, void (*)(int *)> >, const std::piecewise_construct_t &, std::tuple<int &&>, std::tuple<> >' requested here
_Alloc_traits::construct(_M_get_Node_allocator(),
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/bits/stl_tree.h:654:4: note: in instantiation of function template specialization 'std::_Rb_tree<int, std::pair<const int, std::unique_ptr<int, void (*)(int *)> >, std::_Select1st<std::pair<const int, std::unique_ptr<int, void (*)(int *)> > >, std::less<int>, std::allocator<std::pair<const int, std::unique_ptr<int, void (*)(int *)> > > >::_M_construct_node<const std::piecewise_construct_t &, std::tuple<int &&>, std::tuple<> >' requested here
_M_construct_node(__tmp, std::forward<_Args>(__args)...);
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/bits/stl_tree.h:2414:19: note: in instantiation of function template specialization 'std::_Rb_tree<int, std::pair<const int, std::unique_ptr<int, void (*)(int *)> >, std::_Select1st<std::pair<const int, std::unique_ptr<int, void (*)(int *)> > >, std::less<int>, std::allocator<std::pair<const int, std::unique_ptr<int, void (*)(int *)> > > >::_M_create_node<const std::piecewise_construct_t &, std::tuple<int &&>, std::tuple<> >' requested here
_Link_type __z = _M_create_node(std::forward<_Args>(__args)...);
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/bits/stl_map.h:518:15: note: in instantiation of function template specialization 'std::_Rb_tree<int, std::pair<const int, std::unique_ptr<int, void (*)(int *)> >, std::_Select1st<std::pair<const int, std::unique_ptr<int, void (*)(int *)> > >, std::less<int>, std::allocator<std::pair<const int, std::unique_ptr<int, void (*)(int *)> > > >::_M_emplace_hint_unique<const std::piecewise_construct_t &, std::tuple<int &&>, std::tuple<> >' requested here
__i = _M_t._M_emplace_hint_unique(__i, std::piecewise_construct,
^
<source>:11:6: note: in instantiation of member function 'std::map<int, std::unique_ptr<int, void (*)(int *)>, std::less<int>, std::allocator<std::pair<const int, std::unique_ptr<int, void (*)(int *)> > > >::operator[]' requested here
m[0] = std::unique_ptr<int, decltype(&deleter)>(i, &deleter);
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/bits/unique_ptr.h:191:12: note: candidate template ignored: substitution failure [with _Up = void (*)(int *)]: no type named 'type' in 'std::enable_if<false, void>'
constexpr unique_ptr() noexcept
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/bits/unique_ptr.h:204:2: note: candidate constructor template not viable: requires single argument '__p', but no arguments were provided
unique_ptr(pointer __p) noexcept
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/bits/unique_ptr.h:236:12: note: candidate constructor template not viable: requires 1 argument, but 0 were provided
constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { }
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/bits/unique_ptr.h:255:2: note: candidate constructor template not viable: requires single argument '__u', but no arguments were provided
unique_ptr(unique_ptr<_Up, _Ep>&& __u) noexcept
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/bits/unique_ptr.h:265:2: note: candidate constructor template not viable: requires single argument '__u', but no arguments were provided
unique_ptr(auto_ptr<_Up>&& __u) noexcept;
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/bits/unique_ptr.h:241:7: note: candidate constructor not viable: requires single argument '__u', but no arguments were provided
unique_ptr(unique_ptr&& __u) noexcept
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/bits/unique_ptr.h:394:7: note: candidate constructor not viable: requires 1 argument, but 0 were provided
unique_ptr(const unique_ptr&) = delete;
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/bits/unique_ptr.h:215:7: note: candidate constructor not viable: requires 2 arguments, but 0 were provided
unique_ptr(pointer __p,
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/bits/unique_ptr.h:227:7: note: candidate constructor not viable: requires 2 arguments, but 0 were provided
unique_ptr(pointer __p,
^
1 error generated.
Compiler returned: 1
Ответы
Ответ 1
Проблема заключается в том, что m[0]
вызывает конструктор по умолчанию для std::unique_ptr<int, decltype(&deleter)>
, который недоступен, поскольку он требует указателя делетера.
Fix:
struct Deleter {
void operator()(int* i) { delete i; }
};
std::map<int, std::unique_ptr<int, Deleter>> m;
void foo(int* i) {
m[0] = std::unique_ptr<int, Deleter>(i);
}
Это также более эффективно, чем std::unique_ptr<int, decltype(&deleter)>
потому что ему не нужно хранить тот же самый указатель, который будет deleter
в каждом экземпляре std::unique_ptr
. Ie sizeof(std::unique_ptr<int, Deleter>) < sizeof(std::unique_ptr<int, decltype(&deleter)>)
.
Ответ 2
std::unique_ptr
, при использовании пользовательской функции удаления, как и у вас, не является конструктивным по умолчанию. std::map::operator[]
требует, чтобы тип значения карты был по умолчанию конструктивным. Это значит, что вы не можете использовать operator []
с этим типом карты.
Для того чтобы std::unique_ptr
по умолчанию конструктивным, вам нужен делеттер, который удовлетворяет тем, где std::is_default_constructible<Deleter>::value
is true
а Deleter
не является типом указателя.