web-dev-qa-db-fra.com

Stockage d'un unique_ptr avec deleter personnalisé dans une carte

Pourquoi ça ne marche pas?

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

Découvrez l'erreur de compilation incompréhensible 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
27
Timmmm

Le problème est que m[0] Appelle le constructeur par défaut de std::unique_ptr<int, decltype(&deleter)>, qui n'est pas disponible car il nécessite le pointeur deleter.

Réparer:

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

C'est aussi plus efficace que std::unique_ptr<int, decltype(&deleter)> car il n'a pas besoin de stocker le même pointeur sur deleter dans chaque instance de std::unique_ptr. C'est à dire. sizeof(std::unique_ptr<int, Deleter>) < sizeof(std::unique_ptr<int, decltype(&deleter)>).

28
Maxim Egorushkin

std::unique_ptr, lorsque vous utilisez une fonction de suppression personnalisée comme la vôtre, n'est pas constructible par défaut. std::map::operator[] requiert que le type de valeur de la carte soit constructible par défaut. Cela signifie que vous ne pouvez pas utiliser operator [] avec ce type de carte.

Pour std::unique_ptr pour être constructible par défaut, vous avez besoin d'un suppresseur qui satisfait où std::is_default_constructible<Deleter>::value est true et Deleter n'est pas un type de pointeur.

13
NathanOliver