我试图更好地理解std :: unordered_map :: emplace,我想我理解复制和移动构造函数是如何被利用的。我已经概述了以下每种不同用法的相关描述。如果有人发现描述有任何问题,请告诉我。
但是,我最感兴趣的是,只有定义了默认和用户定义的构造函数时会发生什么?看起来默认构造函数根本没有被调用,用户定义的构造函数只被称为ONCE,那么如何在unordered_map中填充新构造元素的FooBar成员呢? (我想象默认/用户定义的构造函数至少被调用两次)。另外,如果FooBar没有定义复制和移动构造函数,那么下面3个案例是否有任何行为差异?
注意:我知道这是一个简单的例子,深度拷贝不是一个问题,因此复制/移动语义并没有真正产生任何显着的收益。我只是用这个简化的例子来说明我的观点。
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;
}
感谢。
答案 0 :(得分:4)
您似乎对基本术语有一些误解。 默认构造函数是可以不带参数调用的构造函数。 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
对象。
如果您不希望在将对象放入地图时使用复制/移动构造函数,则使用std::pair
的分段构造构造函数。
map.emplace(std::piecewise_construct,
std::forward_as_tuple(40),
std::forward_as_tuple((int*)0xDEADBEEF, (int*)0x01010101));
这应该打印一行
Foobar default constructor called