我正在努力更好地理解 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
关于c++ - 没有移动/复制构造函数的 std::unordered_map::emplace 行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27698968/