【问题标题】:Insert in unordered map calls constructor插入无序映射调用构造函数
【发布时间】:2015-04-28 19:09:48
【问题描述】:

为了避免元素重复,我正在构建一个包含元素并提供对它们的访问权限的类。

我的元素 (DynLibrary) 可移动但不可复制

class DynLibrary
{
    public:
        DynLibrary() : _handle(nullptr) {}
        DynLibrary(const std::string& path) { DynLibrary::open(path); }
        DynLibrary(const DynLibrary&) = delete;
        DynLibrary(DynLibrary&&) = default;
        ~DynLibrary() { DynLibrary::close(); }
    ...
}

这些对象在unordered_map 中分配,其中键是生成它们的路径。 我就是这样分配的

class DynAllocator
{
    public:
        DynLibrary& library(const std::string& f)
        {
            if (_handles.find(f) == _handles.end())
            {
                std::cout << "@Emplace" << std::endl;
                _handles.emplace(f, DynLibrary(f));
            }
            std::cout << "@Return" << std::endl;
            return _handles.at(f);
        }
    private:
        std::unordered_map<std::string, DynLibrary> _handles;
};

但是,当调用 DynAllocator::library 时,我得到以下输出:

@Emplace
close 0x1dfd1e0 // DynLibrary destructor
@Return

这意味着插入的对象以某种方式被复制并且副本的析构函数刚刚使我的对象无效(使用我的处理程序调用dlclose

  • 我的DynLibrary 的可移动但不可复制的方法好吗?
  • 如果我的unordered_map 没有副本,我如何插入DynLibrary 的实例?

请注意,我知道如何使用指针/智能指针 (std::unique_ptr) 来做到这一点,但我想不惜一切代价避免它们!

【问题讨论】:

    标签: c++ c++11 containers destructor rvalue


    【解决方案1】:

    这意味着插入的对象以某种方式被复制,而副本的析构函数使我的对象无效

    不,这不是那个意思。 DynLibrary 有一个 deleted 复制构造函数,因此如果通过重载决议以某种方式选择了该构造函数,则代码将无法编译。

    _handles.emplace(f, DynLibrary(f));
    

    上面一行发生的事情是您正在创建一个临时的DynLibrary 对象,然后将其移动构造unordered_map 中。如果您希望避免这种移动构造,请改用std::piecewise_construct

    _handles.emplace(std::piecewise_construct,
                     std::forward_as_tuple(f),
                     std::forward_as_tuple(f));
    

    现在您直接在unordered_map 中构造DynLibrary 对象并绕过临时创建。


    作为 T.C. comments,在这种情况下不需要分段构造构造函数,因为DynLibrary 有一个非explicit 转换构造函数。您可以使用

    实现与上述相同的效果
    _handles.emplace(f, f);
    

    【讨论】:

    • 谢谢,那么 std::piecewise_construct 方法效果很好。仍然我想知道为什么有人想调用析构函数被调用临时?
    • @Amxx:如果你有一个唯一的所有权资源,你应该让被移动的对象不破坏它的析构函数中的资源。
    • @Amxx 您构造了一个临时对象,然后将其用作unordered_map 中移动构造的源对象。完成后,无论是否移出,都需要销毁临时对象。
    • @Amxx Puppy 在他的评论中有一个很好的观点。根据_handle 数据成员的类型,默认的移动构造函数可能不够用。例如,如果它是void *,则需要定义移动构造函数并将源对象的_handle 设置为nullptr,以避免它在源对象的析构函数中被销毁。
    • 对于 OP 中显示的代码,您真的需要 piecewise_construct 吗? pair( U1&amp;&amp; x, U2&amp;&amp; y ); 构造函数应该处理它。 (当然,如果 DynLibrary(const std::string&amp; path)explicit,你会需要它......)
    猜你喜欢
    • 2013-04-03
    • 2021-09-26
    • 1970-01-01
    • 2021-11-16
    • 2015-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多