C2338: стандарт C++ не предоставляет хэш для этого типа со строкой и unique_ptr.

Я пытаюсь реализовать std::unordered_map с std::string в качестве ключа и std::unique_ptr в качестве значения. Однако, когда я пытаюсь скомпилировать, я получаю сообщение об ошибке:

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

Просматривая разные вопросы, я знаю, что С++ 11 действительно включает std::hash ‹ std::string >, и я не вижу причин, по которым эта ошибка может быть выдана. Я попытался реализовать свою собственную хеш-функцию, подобную той, которую видели здесь, но все равно выдает ту же ошибку. Я также пытался использовать __declspec(dllexport) и сделать конструктор копирования и оператор присваивания для содержащего класса закрытым, как это предлагается в некоторых потоках, чтобы заставить работать unique_ptr, но безрезультатно.

Вот код класса-нарушителя:

#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

Я не могу найти причину, по которой это не должно работать, и я пробовал почти все другие решения, которые мог найти, когда искал проблему.

Я использую MSVC 2012.

Любая помощь приветствуется. Спасибо.

EDIT: добавление класса AtlasTexture: header и реализация

РЕДАКТИРОВАТЬ: Моя реализация перемещения и задания перемещения: здесь.


person sm81095    schedule 13.02.2014    source источник
comment
Ваш ключ _registeredIcons не должен быть const — просто старый добрый std::string (hash предназначен для string, а не const string). Кроме того, std::hash(const char*) будет хэшировать адрес указателя, а не текст, на который он указывает.... это действительно то, что вы хотите?   -  person Tony Delroy    schedule 13.02.2014
comment
Ну, проблема в том, что без const перед строкой я начинаю получать новую ошибку: error C2248: 'std::unique_ptr‹_Ty›::unique_ptr': невозможно получить доступ к частному члену, объявленному в классе 'std::unique_ptr‹ _Ты›'   -  person sm81095    schedule 13.02.2014
comment
Я думаю, это потому, что unique_ptr<> не является допустимым типом для сопоставления... согласно 23.2.5.10 key_type и mapped_type иногда требуется, чтобы они были CopyAssignable - unique_ptr не является CopyAssignable. Вы можете использовать shared_ptr.   -  person Tony Delroy    schedule 13.02.2014
comment
Если вы действительно считаете, что проблема связана с хешированием, попробуйте что-нибудь попроще, например unordered_map<string, int>. Удалите все другие смешанные факторы (const, unique_ptr).   -  person juanchopanza    schedule 13.02.2014
comment
Я видел примеры кода, использующего unique_ptr в качестве значения карты. Я мог бы немного изменить классы, чтобы использовать shared_ptr, но я действительно предпочел бы избежать этого, если это возможно. Вы абсолютно уверены, что этого делать нельзя?   -  person sm81095    schedule 13.02.2014
comment
Какая строка кода сообщает о новой ошибке?   -  person Tony Delroy    schedule 13.02.2014
comment
@TonyD Я думаю, что иногда очень важно, я думаю, это означает, что некоторые функции-члены могут требовать, чтобы mapped_type было CopyAssignable, но не все. Определенно работает в этом тривиальном случае использования.   -  person Praetorian    schedule 13.02.2014
comment
IIRC, в реализации MSVC есть ошибка, которая запрещает использование unique_ptr в качестве value_type.   -  person Casey    schedule 13.02.2014
comment
@Praetorian: конечно ... Я думаю, что это только в том случае, если вы используете список инициализаторов ... поэтому я попросил показать строку кода, вызывающую ошибку. Раньше в GCC была ошибка с unique_ptr, интересно, что Кейси подозревает и MSVC.   -  person Tony Delroy    schedule 13.02.2014
comment
На самом деле я не могу найти конкретную строку, которая генерирует эту ошибку. Однако часть вывода ошибки: эта диагностика произошла в сгенерированной компилятором функции 'std::pair‹_Ty1,_Ty2›::pair(const std::pair‹_Ty1,_Ty2› &)'   -  person sm81095    schedule 13.02.2014
comment
@TonyD Да, список инициализаторов определенно не сработает, потому что он разрешает только const доступ к своим членам. Я думаю, что у VALOD9 есть правильный ответ ниже. sm81095: Попробуйте реализовать конструктор перемещения и оператор присваивания перемещения для AtlasTexture.   -  person Praetorian    schedule 13.02.2014
comment
Я снимаю свое обвинение: std::unordered_map<std::string, std::unique_ptr<int>> кажется пригодным для использования в VS2013..   -  person Casey    schedule 13.02.2014
comment
@ sm81095 sm81095 Конструктор копирования жалоб пары указывает, что проблема, вероятно, связана с тем, как вы вставляете unique_ptr в карту. Можете ли вы показать нам эту часть кода? (Я полагаю, registerIcon/registerIcons?)   -  person Casey    schedule 13.02.2014
comment
Это самая последняя реализация, с которой я столкнулся: _registeredIcons.insert(std::move(std::make_pair(path, std::unique_ptr<AtlasTexture>(obj))));, где path — это const char*, а obj — это указатель AtlasTexture, созданный всего несколькими строками выше.   -  person sm81095    schedule 13.02.2014
comment
@ sm81095 Это копия: она преобразует результат make_pair - std::pair<const char*,std::unique_ptr<AtlasTexture>> - в value_type карты std::pair<const std::string,std::unique_ptr<AtlasTexture>>. Я предлагаю использовать emplace вместо insert. (Кроме того, бессмысленно std::move результат функции, которая уже является rvalue).   -  person Casey    schedule 13.02.2014
comment
Вот вся функция registerIcons().   -  person sm81095    schedule 13.02.2014
comment
@Casey, тогда как мне правильно вставить unique_ptr?   -  person sm81095    schedule 13.02.2014
comment
@ sm81095 Самый простой способ — использовать emplace. Сложный способ — использовать insert(std::pair<const std::string,std::unique_ptr<AtlasTexture>>(path,std::unique_ptr<AtlasTexture>(obj))). В частности, _registeredIcons.emplace(path, std::unique_ptr<AtlasTexture>(obj));   -  person Casey    schedule 13.02.2014
comment
Я заменил код на тот, что указан выше, но проблема остается. Ошибка постоянно содержит This diagnostic occurred in the compiler generated function 'std::pair<_Ty1,_Ty2>::pair(const std::pair<_Ty1,_Ty2> &)'. Может ли for (auto pair : _registeredIcons) быть причиной этого?   -  person sm81095    schedule 13.02.2014
comment
@ sm81095: да, по умолчанию auto является копией; вы можете указать либо auto&, либо auto const& в зависимости от того, может ли ссылка быть const или нет.   -  person Matthieu M.    schedule 13.02.2014
comment
Хорошо, теперь я могу скомпилировать его, если он в основном работает (продолжает происходить несвязанный сбой). Кажется, это была комбинация замены .insert() на .emplace и замены auto на auto&. Спасибо всем, кто помог.   -  person sm81095    schedule 13.02.2014
comment
@ sm81095 Пожалуйста, создайте ответ и ответьте сами, а также пометьте его как правильный...   -  person user1708860    schedule 13.02.2014


Ответы (2)


Вы пытались реализовать все методы, сгенерированные компиляцией, в AtlasTexture?

person VALOD9    schedule 13.02.2014
comment
Чувак, перестань комментировать, выдавая себя за ответы. Маловероятно, что это как-то связано с AtlasTexture. Эта ошибка, которую он разместил в комментариях выше, скорее всего, означает, что он пытается скопировать конструкцию/копировать, присвоив unique_ptr где-то. - person Praetorian; 13.02.2014
comment
Прошу прощения за предыдущий комментарий. Возможно, вы попали в точку; проблема вполне может заключаться в том, что OP не реализовал конструктор перемещения для AtlasTexture, а MSVC не создает конструкторы перемещения неявно. - person Praetorian; 13.02.2014
comment
Я добавил ссылку на свою реализацию перемещения и задания перемещения. Я не знаком с ними, поэтому я просто реализовал то, что, как мне кажется, делает стандартный конструктор копирования. - person sm81095; 13.02.2014

Моя проблема заключалась в том, как я размещал std::unique_ptr на карте. Лучший способ разместить его — использовать map::emplace() вместо map::insert(). Это связано с тем, что для std::unique_ptr нет конструктора копирования, и emplace перемещает объект, а не копирует его. Спасибо @Casey за этот ответ.

Моя другая проблема заключалась в использовании нового типа auto для получения пар с карты. Опять же, поскольку unique_ptr не может быть скопировано, что происходит, когда auto изменяется на std::pair, это вызывает ошибку компилятора. Хорошим простым решением для этого было использование auto& вместо auto. Благодаря @MatthieuM. для этого.

person sm81095    schedule 13.02.2014