繁体   English   中英

带有自定义键编译器错误的unordered_map

[英]unordered_map with custom key compiler error

我有一个关于std::unordered_map和自定义类的关键问题。
我认为首先需要一些背景:

自定义类是一种变量数据类型,它实现基本的数值类型和std::string类。
最近,我的一个兄弟告诉我,如果该类支持数组和哈希表,那就太好了。 我想“不再说了”,并开始实现效果很好的数组功能(使用std::vector ),然后实现了哈希图功能(使用unordered_map<Variant, Variant> )。

如果我理解正确的话, unordered_map的哈希函数(或分别为operator() )必须符合签名size_t (*) (const Key_Type &k) const; 我的std::hash<Variant>对象的专用版本应该做什么,不是吗?
另外, unordered_map需要检查Key_Type是否相等,这应该可以通过operator==() ,我正确吗?

无论如何,我都会遇到很多漂亮的编译器错误,在我看来,这是最有用的:
/usr/include/c++/4.9/bits/hashtable_policy.h:85:33: error: no match for call to '(const std::hash<Variant>) (const Variant&)'

我真的不明白发生了什么,并且非常感谢对发生的事情的任何见解。

下面是Variant类的精简标题,我希望包含足够的信息(说实话,我担心这是太多信息,但我不确定可以忽略什么)。
但是我省略了大多数实现细节,因为该问题似乎仅在专用哈希对象中发生。

好吧,这是Variant标头的精简版本:

class Variant
{
    private:
        enum Type {NONE = 0, LONG, DOUBLE, STRING, ARRAY, HASH_MAP};
        using Var = struct Var
        {
            union
            {
                int64_t l;
                double d;
                std::string *s;
                std::vector<Variant> *v;
                std::unordered_map<Variant, Variant> *h;
            };
            Type type = NONE;
        };

    public:
        //constructors, destructor and clear function
        Variant() : var() {}
        Variant(long val): Variant(){var.type = LONG; var.l = val;}
        Variant(double val) : Variant(){var.type = DOUBLE; var.d = val;}
        Variant(const std::string &val) : Variant(){var.type = STRING; var.s = new std::string(val);}
        template<typename T, typename... Args>Variant(T val, Args... args) : Variant() {set(val, args...);} //constructs an array

        Variant(const Variant &val); //calls default constructor as well
        Variant(Variant &&val) : Variant() {swap(*this, val);}

        ~Variant(){clear();}
        void clear();

        //set functions
        template<typename T, typename... Args> void set(const T val, Args... args){if(var.type == ARRAY)var.v->clear();add(val, args...);}
        void set(long val);
        void set(double val);
        void set(const std::string &val);
        void set(const Variant &val);

        //add functions
        template<typename T> void add(const T val){add(Variant(val));}
        template<typename T, typename... Args> void add(const T val, Args... args){add(Variant(val)); add(args...);}
        void add(const std::string &val){add(Variant(val));}
        void add(const Variant &val);

        //array access and evaluation functions
        Variant& operator[](const Variant &idx);
        size_t size() const {if(var.type == ARRAY)return var.v->size(); return 0;}
        std::unordered_map<Variant, Variant>::iterator begin(){if(var.type == HASH_MAP)return var.h->begin(); throw Exception("The internal type does not support iterators");}

        //operator= definitions
        template<typename T> Variant& operator=(const T val){set(val); return *this;}
        Variant& operator=(const std::string &val){set(val); return *this;}
        Variant& operator=(Variant val){swap(*this, val); return *this;}

        //operator definitions
        Variant& operator+=(const Variant &right);
        //and operator-=, ^= etc etc...

        //friend definitions (mainly comparison operators)
        friend void swap(Variant &left, Variant &right); //simple swap function
        friend bool operator==(const Variant &left, const Variant &right);
        friend bool operator!=(const Variant &left, const Variant &right);    
        friend std::hash<Variant>;

    private:
        Var var;
};


template <typename T>
inline void hash_combine(std::size_t& seed, const T &v)
{
    std::hash<T> hasher;
    seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}

namespace std
{
    template<> struct hash<Variant>
    {
        size_t operator()(const Variant &x) const
        {
            if(x.var.type == Variant::DOUBLE)
                return std::hash<double>()(x.var.d);
            else if(x.var.type == Variant::LONG)
                return std::hash<int64_t>()(x.var.l);
            else if(x.var.type == Variant::STRING)
                return std::hash<std::string>()(*x.var.s);
            else if(x.var.type == Variant::ARRAY)
            {
                size_t seed = 0;
                for(size_t i = 0; i < x.var.v->size(); ++i)
                    hash_combine(seed, x.var.v->operator[](i));
                return seed;
            }
            else if(x.var.type == Variant::HASH_MAP)
            {
                size_t seed = 0;
                for(auto it = x.var.h->begin(); it != x.var.h->end(); ++it)
                {
                    hash_combine(seed, it->first);
                    hash_combine(seed, it->second);
                }
                return seed;
            }
            else if(x.var.type == Variant::NONE)
                return 0;
            else
                throw std::runtime_error("This Variant cannot be hashed");
        }
    };
}

inline void swap(Variant &left, Variant &right){Variant::Var tmp(left.var); left.var = right.var; right.var = tmp;}

bool operator==(const Variant &left, const Variant &right);
bool operator!=(const Variant &left, const Variant &right);

这里的问题是您在Variant本身的定义内使用了unordered_map<Variant, Variant> 此时,您的hash专用化尚不可用,这就是编译器产生错误的原因。 您不能仅将哈希定义移到Variant定义之前,因为哈希需要访问Variant成员。 您可以做的是将hash声明和定义分开:

class Variant;

namespace std
{
    template<> struct hash<Variant>
    {
        size_t operator()(const Variant & x) const;
    };
}

class Variant {
/* Variant definition goes here ... */
};

template <typename T>
inline void hash_combine(std::size_t& seed, const T &v)
{
    std::hash<T> hasher;
    seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}

size_t std::hash<Variant>::operator()(const Variant &x) const
{
    /* hash function implementation here ... */
}

但是,您还有另一个问题:在Variant类定义中, Variant本身是不完整的类型。 在您的联合中,您仅存储指向vector和unordered_map的指针,这是可以的,但是begin方法(实际上,已经指定了它的返回类型)要求实例化unordered_map<Variant, Variant> ,在该位置是不可能的。

(注意: 对不完整类型的容器 (仅矢量,列表和forward_list)的有限支持将添加到C ++ 17)

要解决第二个问题,可以使用map成员函数代替begin函数,该函数可以访问内部地图:

std::unordered_map<Variant, Variant> & map()
{
    if (var.type == HASH_MAP)
        return *var.h; 
    throw Exception("The internal type does not support iterators");
}

然后代替

Variant v;
v.begin();

你会用

v.map().begin();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM