2
votes

I am trying to implement an std::unordered_map with std::string as the key and std::unique_ptr as the value. However, when I try to compile, I get the error:

error C2338: The C++ Standard doesn't provide a hash for this type.

Looking around at different questions, I know that C++11 does indeed include a std::hash < std::string >, and I can see no reason why this error would be thrown. I've tried to implement my own hashing function, like the one seen here, but it is still throwing the same error. I have also tried using __declspec(dllexport) and making the copy constructor and assignment operator for the containing class private, as it is suggested in some threads to make unique_ptr work, but to no avail.

Here is the code for the offending class:

#ifndef __TEXTURE_MAP_H__
#define __TEXTURE_MAP_H__

#include <unordered_map>
#include <vector>
#include <memory>
#include <string>

//__declspec for std::unique_ptr compat.
class /*__declspec(dllexport)*/ TextureMap : virtual public IconRegister
{
private:
    uint32 _textureId;
    std::unordered_map<const std::string, std::unique_ptr<AtlasTexture> > _registeredIcons;
    std::unordered_map<const char*, AtlasTexture*> _uploadedIcons;
    std::vector<AtlasTexture*> _animatedIcons;

public:
    TextureMap();
    ~TextureMap();

    uint32 getTextureId();

    void loadTextureAtlas();

    /* override */ IIcon& registerIcon(const char*);
    void registerIcons();

private:
    TextureMap(const TextureMap& other) { }
    TextureMap& operator= (const TextureMap& other) { return *this; };
};

#endif

I cannot find any reason this should not be working, and I've tried pretty much every other solution I could find when I searched for the problem.

I am using MSVC 2012.

Any help is greatly appreciated. Thanks.

EDIT: Addition of the AtlasTexture class: header and implementation

EDIT: My implementation of the move and move assignment: here.

2
Your _registeredIcons key shouldn't be const - just plain old std::string (hash is specialised for string not const string). Also, std::hash(const char*) will hash the pointer's address, not the pointed-to text.... is that really what you want?Tony Delroy
Well, the problem is without the const in front of the string, I start getting a new error: " error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>' "sm81095
I think that's because unique_ptr<> isn't a valid type to map to... per 23.2.5.10 "key_type and mapped_type are sometimes required to be CopyAssignable" - unique_ptr is not CopyAssignable. You could use shared_ptr.Tony Delroy
@Praetorian: certainly... I think it's only if you use an initialiser list... that's why I asked to see the line of code provoking the error. GCC used to have a bug with unique_ptr though, interesting that Casey suspects MSVC too.Tony Delroy

2 Answers

1
votes

Have you tried to implement all compile generated methods af AtlasTexture?

1
votes

My issue was with how I was placing the std::unique_ptrs into the map. The best way to place it in is to use map::emplace() instead of map::insert(). This is becasue there is no copy constructor for std::unique_ptr and emplace moves the object instead of copies it. Thanks to @Casey for this answer.

My other issue was using the new auto type in order to get the pairs from the map. Again, becasue the unique_ptr cannot be copied, which happens when auto gets changed to std::pair, this was throwing a compiler error. The nice easy fix for this was to use auto& instead of auto. Thanks to @MatthieuM. for this.