c++ - 没有移动/复制构造函数的 std::unordered_map::emplace 行为

标签 c++ visual-c++ c++11 unordered-map

我正在努力更好地理解 std::unordered_map::emplace,并且我想我理解复制和移动构造函数(如果存在)是如何被利用的。我在下面概述了每种不同用法的相关描述。如果有人发现描述有任何问题,请告诉我。

但是,我最好奇的是,当仅定义默认和用户定义的构造函数时会发生什么?貌似默认构造函数根本没有调用,自定义构造函数只调用了一次,那么它是如何填充unordered_map中新建元素的FooBar成员的呢? (我想象默认/用户定义的构造函数至少被调用两次)。另外,如果 FooBar 没有定义复制和移动构造函数,那么以下 3 种情况是否存在任何行为差异?

注意:我知道这是一个微不足道的例子,其中深拷贝不是问题,所以复制/移动语义并没有真正产生任何显着的 yield 。我只是用这个简化的例子来说明我的观点。

struct FooBar
{
    FooBar()
    {
        printf("Foobar default constructor called\n");
    };

    FooBar(int* pFoo, int* pBar)
    {
        m_pFoo = pFoo;
        m_pBar = pBar;
        printf("Foobar user-defined constructor called\n");
    };

    FooBar(FooBar & rhs)
    {
        m_pBar = rhs.m_pBar;
        m_pFoo = rhs.m_pFoo;
        printf("Foobar copy constructor called\n");
    };

    FooBar(FooBar && rhs)
    {
        m_pBar = rhs.m_pBar;
        m_pFoo = rhs.m_pFoo;
        rhs.m_pBar = nullptr;
        rhs.m_pFoo = nullptr;
        printf("Foobar move constructor called\n");
    };

    int* m_pFoo;
    int* m_pBar;
};


int _tmain(int argc, _TCHAR* argv[])
{
    std::unordered_map<int, FooBar> map;

    //template< class... Args >
    //std::pair<iterator, bool> emplace(Args&&... args);

    // 1. 
    // Description: A lvalue of foobar1 is temporarily created, initialized, copied (via copy constructor)
    // to supply the in-place constructed element's FooBar member, and destroyed
    // Output (if both copy and move constructor exist): Foobar user-defined constructor called, Foobar copy constructor called
    // Output (if both copy and move constructor don't exist): Foobar user-defined constructor called
    {
        FooBar foobar1 = {(int*)0xDEADBEEF, (int*)0x01010101};
        map.emplace(10, foobar1);
    }

    // 2. 
    // Description: A rvalue of bar1 is temporarily created, initialized, moved (via move constructor)
    // to supply the in-place constructed element's FooBar member, and destroyed
    // Output (if both copy and move constructor exist): Foobar user-defined constructor called, Foobar move constructor called
    // Output (if both copy and move constructor don't exist): Foobar user-defined constructor called
    map.emplace(20, FooBar{(int*)0xDEADBEEF,(int*)0x01010101});

    // 3. 
    // Description: A lvalue of foobar1 is temporarily created and initialized. It is then
    // explicitly converted to a rvalue (via std::move), moved (via move constructor) to supply 
    // the in-place constructed element's FooBar member, and destroyed
    // Output (if both copy and move constructor exist): Foobar user-defined constructor called, Foobar move constructor called
    // Output (if both copy and move constructor don't exist): Foobar user-defined constructor called
    {
        FooBar foobar2 = {(int*)0xDEADBEEF, (int*)0x01010101};
        map.emplace(30, std::move(foobar2));
    }

    return 0;
}

谢谢。

最佳答案

您似乎对基本术语有一些误解。 默认构造函数 是可以不带参数调用的构造函数。 FooBar(int* pFoo, int* pBar)不是默认构造函数。

此外,为了让您的代码在 gcc 和 clang 上编译,我必须将您的复制构造函数修改为 FooBar(FooBar const& rhs) .

现在,当您在评论中说复制/移动构造函数不存在时,我怀疑您只是在删除它们的定义。但这并不意味着它们不存在,编译器会为您隐式定义一个(请注意,VS2013 不会隐式定义移动构造函数,因为它缺少该功能)。

当您调用 unordered_map::emplace 时, 参数被转发以构造一个 unordered_map::value_type对象,即 std::pair<const Key, Value> , 所以调用 emplace最后调用 std::pair constructor .

在情况 1 中,您创建了一个名为 foobar1 的对象, 以及对 emplace 的调用打电话 FooBar复制构造函数。

在情况 2 中,临时 FooBar您创建的对象将被移动,即 FooBar移动构造函数被调用,假设一个存在。如果没有,将调用复制构造函数。

案例 3 与案例 2 相同,因为通过调用 std::move您允许调用移动构造函数,并且对于 foobar2要移动的对象。


如果在将对象放入 map 时不希望使用复制/移动构造函数,则使用 std::pair 的分段构造构造函数.

map.emplace(std::piecewise_construct,
            std::forward_as_tuple(40),
            std::forward_as_tuple((int*)0xDEADBEEF, (int*)0x01010101));

这应该打印一行

Foobar default constructor called

Live demo

关于c++ - 没有移动/复制构造函数的 std::unordered_map::emplace 行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27698968/

相关文章:

c++ - 为 C++/Java 绘制类层次结构的工具

c++ - 类中数据成员的大小不匹配

c++11 - C++ 中的 Getter 和 Setter

c++ dom解析器问题

c++ - boost condition_variable 等待多个锁

c++ - C++ 中成员函数的 const& 、 & 和 && 说明符

c++ - 为什么 C++11 仍然强制可见性的词法排序

c++ - 为什么我不能在我的程序中声明一个字符串 : "string is undeclared identifier"

c++ - 检查两个(智能)指针是否指向同一个派生类

C++:如何在非零大小的 map 中初始化 vector