lundi 29 décembre 2014

std::unordered_map::emplace behavior with only default constructor

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 constructor is defined? It looks like the default 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 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(int* pFoo, int* pBar)
{
m_pFoo = pFoo;
m_pBar = pBar;
printf("Foobar default 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 default constructor called, Foobar copy constructor called
// Output (if both copy and move constructor don't exist): Foobar default 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 default constructor called, Foobar move constructor called
// Output (if both copy and move constructor don't exist): Foobar default 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 default constructor called, Foobar move constructor called
// Output (if both copy and move constructor don't exist): Foobar default constructor called
{
FooBar foobar2 = {(int*)0xDEADBEEF, (int*)0x01010101};
map.emplace(30, std::move(foobar2));
}

return 0;
}


Thanks.


Aucun commentaire:

Enregistrer un commentaire