c++ - make_unique - shared_ptr




Almacenar un unique_ptr con un borrado personalizado en un mapa (2)

¿Por qué no funciona esto?

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

Verifique el error de compilación incomprensible 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

El problema es que m[0] llama al constructor predeterminado de std::unique_ptr<int, decltype(&deleter)> , que no está disponible porque requiere el puntero de deleter.

Fijar:

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

Esto también es más eficiente que std::unique_ptr<int, decltype(&deleter)> porque no tiene que almacenar el mismo puntero para deleter cada instancia de std::unique_ptr . Por ejemplo, sizeof(std::unique_ptr<int, Deleter>) < sizeof(std::unique_ptr<int, decltype(&deleter)>) .


std::unique_ptr , cuando se usa una función de eliminación personalizada como la que tiene, no se puede construir por defecto. std::map::operator[] requiere que el tipo de valor del mapa sea construible por defecto. Eso significa que, como tal, no puede usar el operator [] con este tipo de mapa.

Para que std::unique_ptr pueda construirse por defecto, necesita un deleter que satisfaga donde std::is_default_constructible<Deleter>::value sea true y Deleter no sea un tipo de puntero.







unique-ptr