【问题标题】:Storing a unique_ptr with custom deleter in a map在地图中使用自定义删除器存储 unique_ptr
【发布时间】:2018-10-05 15:44:22
【问题描述】:

为什么这不起作用?

#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

【问题讨论】:

  • 我不知道为什么这会遭到反对,这是一个好问题。也许是链接。人们可能会将其视为外部代码的链接。为了让其他读者清楚,这是 Godbolt 的实时样本。编辑:我已经编辑了问题以使链接的性质更加明显。
  • 你需要在这里发布错误。
  • 它们很长,只需点击一下即可。这似乎太过分了。
  • 在移动设备上查看 Godbolt 的错误非常困难
  • 由于错误不在问题中,它使问题依赖于godbolt。我已经对它们进行了编辑。Timmmm 您对它们过多的担忧是有道理的,但是 StackOverflow 确实为长代码 sn-ps 添加了一个滚动条。如果您可以将其仅分解为您感到困惑的部分,请随意这样做。

标签: c++ unique-ptr


【解决方案1】:

问题是m[0]调用了std::unique_ptr&lt;int, decltype(&amp;deleter)&gt;的默认构造函数,由于需要deleter指针,所以不可用。

修复:

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&lt;int, decltype(&amp;deleter)&gt; 更有效,因为它不必在std::unique_ptr 的每个实例中存储指向deleter 的完全相同的指针。 IE。 sizeof(std::unique_ptr&lt;int, Deleter&gt;) &lt; sizeof(std::unique_ptr&lt;int, decltype(&amp;deleter)&gt;).

【讨论】:

  • 有趣的事实:std::integral_constant 可以(不明显)担任这个角色:template &lt;auto f&gt; using deleter_fn = std::integral_constant&lt;decltype(deleter_fn), deleter_fn&gt;; std::unique_ptr&lt;int, deleter_fn&lt;deleter&gt;&gt;;
  • @Justin 不错。虽然std::is_integral&lt;decltype(deleter)&gt;::valuefalse。因此,std::integral_constant 中的 integral 具有误导性。
  • @Justin 同样,代理电话有点破坏了聚会,正如smarter souls 向我指出的coliru.stacked-crooked.com/a/759ea11362b1d2de
【解决方案2】:

std::unique_ptr,当使用像你一样的自定义删除函数时,默认不是可构造的。 std::map::operator[] 要求映射的值类型是默认可构造的。这意味着您不能将operator [] 与这种类型的地图一起使用。

为了使std::unique_ptr 成为默认可构造的,您需要一个满足std::is_default_constructible&lt;Deleter&gt;::valuetrue 并且Deleter 不是指针类型的删除器。

【讨论】:

  • 您也可以完全使用 m.insert_or_assign(0, std::unique_ptr&lt;int, decltype(&amp;deleter)&gt;(i, &amp;deleter)); 之类的东西跳过默认构造函数 - 也就是说,我个人绝对更喜欢 Maxim Egorushkin 的答案中的空删除器对象。
猜你喜欢
  • 1970-01-01
  • 2013-03-30
  • 1970-01-01
  • 1970-01-01
  • 2018-01-02
  • 2015-08-13
  • 2023-03-11
  • 2016-11-23
  • 1970-01-01
相关资源
最近更新 更多