jeudi 11 juillet 2019

C++: Why does constructing an std::unordered_map use the value_type's default constructor?

I'm having some trouble understanding the implementation of std::unordered_map.

I have a struct that uses a custom allocator, for example:

struct Family
{
   Family()
   {}

   Family(MyAllocator alloc)
     : memberNames(alloc)
   {}

private:
   std::vector<std::string, MyAllocator> memberNames;
}

And I have another struct that uses an std::unordered_map that maps a uint32_t to the Family struct:

using allocator_unordered_map
    = std::unordered_map<
      uint32_t,                 // Family ID.
      Family,                   // Family.
      std::hash<uint32_t>,
      std::equal_to<uint32_t>,
      MyAllocator>;

struct FamilyClub
{
   FamilyClub(MyAllocator alloc)
     : m_Families(alloc)
   {}

private:
   allocator_unordered_map m_Families;
}

I understand that an std::unordered_map's value_type needs to provide a default constructor when using the operator []. However, since I don't plan on using those operators, the default constructor should not be needed. Still, if I don't include the Family's default constructor, I get the compiler error "C2512: no appropriate default constructor available".

In order to find where was the default constructor being used, I added an assertion inside of it. I found out that it's being called when initializing the unordered_map m_Families, which I find strange because the map's size is still zero after that, meaning that no object was created.

I wondered if that would also happen if I created another unordered_map of the exact same type in the code, such as:

int main()
{
   MyAllocator allocator;

   FamilyClub familyClub(allocator);             // Uses Family's default constructor.

   allocator_unordered_map familyMap(allocator); // Uses Family's parametrized constructor.
}

And I found out that, for some reason, when creating a FamilyClub object, which uses the maps allocator constructor, the Family's default constructor is used, but when creating the exact same map, with the exact same constructor, the Family's allocator constructor is used.

Is there a reason why the std::unordered_map behaves this way? Does anyone know if there's a way in which I can avoid using the default constructor?

Aucun commentaire:

Enregistrer un commentaire