I am trying to gain a better understanding of std::unordered_map::emplace, and I think I understand how copy and move constructors are being utilized if they exist. I have outlined the relevant descriptions for each different usages below. If anyone finds any issues with the descriptions, please let me know.
However, what I am mostly curious about is that, what happens when ONLY the default and user-defined constructors are defined? It looks like the default constructor is not called at all, and the user-defined constructor is only called ONCE, so how is it populating the FooBar member of the newly constructed element in the unordered_map? (I'd imagine the default/user-defined constructor to be called at least twice). Also, if FooBar doesn't define copy and move constructors, are there any behavioral differences for the 3 cases below?
Note: I understand that this is a trivial example where deep copies aren't an issue, so copy/move semantics doesn't really yield any significant gain. I am just using this simplified example to get my point across.
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;
}
Thanks.
FooBar(int* pFoo, int* pBar)is not a "default" constructor. - John ZwinckFooBar(int* pFoo, int* pBar)when the newly-constructed element in the map is created? If so, how do I override that constructor to see the message? I tried justFooBar()and that didn't get called too. - lancery