void.pointer
void.pointer

Reputation: 26335

Compiler error with unordered_map when used in template class

Since my example is somewhat complex, I have put a sample here that demonstrates the error (Code will be inlined below as well): http://coliru.stacked-crooked.com/a/a426995302bda656

#include <functional>
#include <unordered_map>
#include <memory>

enum class DeliveryMethodType
{
  POST_MASTER,
  BUBBLE,
};

class IDeliveryMethod
{
};

class BubbleDelivery : public IDeliveryMethod
{
};

template<typename Key, typename Interface>
class GenericFactory
{
public:

   using FactoryMethod = std::function<std::unique_ptr<Interface> ()>;

   static Key const& Register(Key const& key, FactoryMethod creator)
   {
      s_factoryMethods.insert({key, creator});
      return key;
   }

   static std::unique_ptr<Interface> Create(Key const& key)
   {
      std::unique_ptr<Interface> obj;

      auto it = s_factoryMethods.find(key);
      if (it != s_factoryMethods.end())
      {
         obj = it->second();
      }

      return obj;
   }

private:
   static std::unordered_map<Key, FactoryMethod> s_factoryMethods;
};

template<typename Key, typename Interface>
std::unordered_map<Key, typename GenericFactory<Key, Interface>::FactoryMethod>
GenericFactory<Key, Interface>::s_factoryMethods;

using DeliveryMethodFactory = GenericFactory<DeliveryMethodType, IDeliveryMethod>;

static auto key = DeliveryMethodFactory::Register(DeliveryMethodType::BUBBLE, []() {
      return std::unique_ptr<IDeliveryMethod>(new BubbleDelivery);
});

int main()
{
}

My design goal here is to create a generic static factory class. Each translation unit will (at static initialization time) invoke the Register() method for a specific specialization of GenericFactory for the desired key and factory method types.

I'm getting the following compiler error, which I'm not sure how to resolve.

error: implicit instantiation of undefined template 'std::hash<DeliveryMethodType>'

I imagine perhaps that my template trickery is failing here and I'm not doing something right. Can anyone help identify the issue here? Thanks.

Upvotes: 1

Views: 2277

Answers (3)

Mike Seymour
Mike Seymour

Reputation: 254431

The key type for an unordered container needs either a specialisation of std::hash defined for it, or a custom hash function provided to the container.

Since your container is in a template, with a generic key, it's tricky to provide a custom function; so provide a specialisation, for example:

namespace std {
    template <> struct hash<DeliveryMethodType> {
        size_t operator()(DeliveryMethodType x) const {
            return hash<int>()(static_cast<int>(x));
        }
    };
}

Upvotes: 2

TNA
TNA

Reputation: 2745

For unordered_map you have to provide a hash function for you user defined key-type either by providing a implementation of std::hash<DeliveryMethodType> ob by providing your own hash-function as an constructor argument to unordered_map.

Upvotes: 0

Steve Townsend
Steve Townsend

Reputation: 54138

unordered_map has a default third parameter which is the hasher for the class contained in the container. Sometimes there is a default implementation of this, eg. for int or std::string - if not, you have to implement this.

Upvotes: 0

Related Questions