无法专门化 std::hash 以unordered_map存储自定义类型

Cannot specialize std::hash to store custom type in unordered_map

本文关键字:map 存储 类型 自定义 unordered 专门化 std hash      更新时间:2023-10-16

我正在尝试使用共享库中定义的类的实例作为unordered_map的键。但是当我以常规、正确的方式为这个名为 Tile 的类专门使用 std::hash 模板时,出于某种原因,编译器一直在抱怨:

/usr/include/c++/7/bits/hashtable_policy.h:87: error: no match for call to ‘(const std::hash<Tile>) (const Tile&)’
  noexcept(declval<const _Hash&>()(declval<const _Key&>()))>
           ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~

这是我在其中定义 std::hash ( pathfinder.h 的专用化的头文件(:

#ifndef PATHFINDER_H
#define PATHFINDER_H
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <memory>
#include "Libworld/world.h"
#include "node.h"
#include "nodifier.h"
#include "pathgenerator.h"
namespace std {
    template <>
    struct hash < Tile > {
        public:
            size_t operator() ( const Tile & theTile ) const noexcept {
               return ( ( 32768 * theTile.getXPos ( ) ) + theTile.getYPos ( ) );
            }
            size_t operator() ( const Tile * theTile ) const noexcept {
               return ( ( 32768 * theTile->getXPos ( ) ) + theTile->getYPos ( ) );
            }
    };
    template <>
    struct equal_to < Tile > {
        bool operator ( ) ( const Tile & lhs, const Tile & rhs ) const {
            return ( lhs == rhs );
        }
    };
}
class Pathfinder {
    private:
        std::vector < std::unique_ptr < Tile > > theWorld;
        QString theWorldName;
        int worldRows, worldColumns, numberOfEnemies, numberOfHealthPacks;
        std::unordered_set < std::shared_ptr < Node > > worldNodes;
        std::unordered_map < Tile, bool > hasEnemyOrHealthPack;
        std::shared_ptr < Nodifier > worldNodifier;
        std::shared_ptr < PathGenerator > worldPathGenerator;
        Tile startingTile, endingTile;
        std::shared_ptr < Node > startingNode, endingNode;
        // unsigned int findTileWithLocation ( std::vector < std::unique_ptr < Node > > * nodesList, int x, int y ) const;
        void generateNodes ( );
        void generateHasEnemyOrHealthPack ( World worldLoader, int numberOfEnemies, int numberOfHealthPacks );
public:
    Pathfinder ( QString initialWorldName,
                 Tile initialStartingTile,
                 Tile initialEndingTile,
                 std::shared_ptr < Nodifier > initialWorldNodifier,
                 std::shared_ptr < PathGenerator > initialWorldPathGenerator,
                 int initialNumberOfEnemies,
                 int initialNumberOfHealthPacks );
    QString getWorldName ( ) const;
    void loadNewWorld ( const QString & newWorldName );
    void loadNewWorld ( const QString & newWorldName, const Tile & newWorldStartingTile, const Tile & newWorldEndingTile );
    Tile getStartingTile ( ) const;
    void setStartingTile ( const Tile & newStartingTile );
    Tile getEndingTile ( ) const;
    void setEndingTile ( const Tile & newEndingTile );
    std::shared_ptr < Nodifier > getWorldNodifier ( ) const;
    void setWorldNodifier ( const std::shared_ptr < Nodifier > & newWorldNodifier );
    Tile getTileFromWorld ( int x, int y );
    void printNodes ( );
    void printWorld ( );
    std::list < std::pair <int, int> > generatePath ( );
};
#endif

这是包含 Tile 定义的共享库的头文件:

#ifndef WORLD_H
#define WORLD_H
#include "world_global.h"
#include <vector>
#include <memory>
#include <QObject>
#include <QImage>
class Tile
{
public:
    Tile(int xPosition, int yPosition, float tileWeight);
    virtual ~Tile() =default;
    float getValue() const {return value;};
    void setValue(float newValue) {value = newValue;};
    int getXPos() const {return xPos;};
    int getYPos() const {return yPos;};
    void setXPos(int newPos) {xPos = newPos;};
    void setYPos(int newPos) {yPos = newPos;}
    bool operator== (const Tile & other) const
        {return (getXPos() == other.getXPos()) && (getYPos() == other.getYPos());};
protected:
    int xPos;
    int yPos;
    float value;
};
class Enemy : public Tile
{
public:
    Enemy(int xPosition, int yPosition, float strength);
    virtual ~Enemy() = default;
    bool getDefeated() const {return defeated;}
    void setDefeated(bool value) {defeated = value;};
private:
    bool defeated; //false by construction
};
class PEnemy: public QObject, public Enemy
{
    Q_OBJECT
public:
    PEnemy(int xPosition, int yPosition, float strength);
    virtual ~PEnemy() = default;
    float getPoisonLevel() const;
    void setPoisonLevel(float value);
public slots:
    bool poison();
signals:
    void dead();
    void poisonLevelUpdated(int value);
private:
    float poisonLevel;
};
class Protagonist: public QObject, public Tile
{
    Q_OBJECT
public:
    Protagonist();
    void setXPos(int newPos) {xPos = newPos; emit posChanged(xPos, yPos);}
    void setYPos(int newPos) {yPos = newPos; emit posChanged(xPos, yPos);}
    void setPos(int newX, int newY) {if (xPos != newX || yPos != newY) {xPos = newX; yPos = newY; emit posChanged(xPos, yPos);}}
    float getHealth() const {return health;};
    void setHealth(float value) {health = value;}
    float getEnergy() const {return energy;}
    void setEnergy(float value) {energy = value;}
signals:
    void posChanged(int x, int y);
private:
    float health; //100.0f by construction
    float energy; //100.0f by construction
};
class WORLDSHARED_EXPORT World
{
public:
    World() = default;
    //to obtain non-overlapping enemies and healthpacks you should call the following 3 methods in this order
    std::vector<std::unique_ptr<Tile>> createWorld(QString filename);
    std::vector<std::unique_ptr<Enemy>> getEnemies(unsigned int nrOfEnemies);
    std::vector<std::unique_ptr<Tile>> getHealthPacks(unsigned int nrOfPacks);
    std::unique_ptr<Protagonist> getProtagonist();
    int getRows() const {return rows;};
    int getCols() const {return cols;};
private:
    int rows, cols;
    QImage world;
    std::vector<QPoint> used; //only for internal use
};

#endif // WORLD_H

我确定我做的一切都是正确的,所以如果有人能告诉我为什么这不编译,我会很高兴?

提前非常感谢,我已经坚持了很长时间。

此致敬意乔舒亚

如果我将 templahttps://stackoverflow.com/help/how-to-answerte 专用化移动到定义 Tile 类的头文件中(当然是在类的定义之后,但在此之后的任何地方(,代码就会编译。

通常,当您自己也实现类时,这无关紧要。我认为这与在共享库中实现的这个类有关。我不是C++大师,所以我无法解释这一点。但我很想在这里有人仍然解释它。