unordered_set将元素存储为指针

时间:2012-04-08 13:07:35

标签: c++ pointers hash equality unordered

缩小范围:我目前正在使用Boost.Unordered。我看到两种可能的解决方案:

  1. 定义我自己的Equality Predicates and Hash Functions并利用模板(可能is_pointer)区分指针和实例;

  2. 只需通过提供boost::hash来扩展hash_value(Type* const& x),就像哈希一样;并使用==参数将(Type* const& x, Type* const& y)运算符重载作为自由函数添加,以进行相等性检查。

  3. 我不确定这两种变化是否真的可行,因为我没有测试它们。我想找出你处理这个问题。欢迎实施:)

    编辑1: 那怎么样?

    template<class T>
    struct Equals: std::binary_function<T, T, bool> {
        bool operator()(T const& left, T const& right) const {
            return left == right;
        }
    };
    
    template<class T>
    struct Equals<T*> : std::binary_function<T*, T*, bool> {
        bool operator()(T* const& left, T* const& right) const {
            return *left == *right;
        }
    };
    

    编辑2:

    我刚刚定义:

    friend std::size_t hash_value(Base const& base) {
        boost::hash<std::string> hash;
    
        return hash(base.string_);
    }
    
    friend std::size_t hash_value(Base* const& base) {
        return hash_value(*base);
    }
    

    然后:

    Derived d1("x");
    Derived d2("x");
    
    unordered_set<Base*> set;
    
    set.insert(&d1);
    
    assert(set.find(&d2) == end());
    

    调试器说永远不会调用friend std::size_t hash_value(Base* const& base)(GCC 4.7)。那是为什么?

    编辑3: 我发现第#215行({1}}中的template <class T> std::size_t hash_value(T* const& v)(Boost 1.49)是Boost对指针的专精化,它只是掩盖了我在编辑2中的boost/functional/hash.hpp的自定义实现即可。 因此,似乎这里唯一的方法是创建一个自定义Hash Functor。

2 个答案:

答案 0 :(得分:5)

对于散列函数,您可以选择在新标准中专门化boost::hash(或std::hash)或定义新的仿函数类。这些替代方案同样有效。

对于等于运算符,您需要定义一个新的仿函数,因为您无法通过指针重新定义等于运算符。它是一个内置的运算符(在函数术语中定义为bool operator==( T const *x, T const *y )),不能替换。

通过在非模板化类中使用模板operator(),可以一般性地定义这两个。

struct indirect_equal {
    template< typename X, typename Y >
    bool operator() ( X const &lhs, Y const &rhs )
        { return * lhs == * rhs; }
};

遵循类似的幽默模式。

答案 1 :(得分:1)

考虑到原始帖子中的所有编辑,我想提供满足我需求的完整解决方案:

<强> 1。平等:

template<class T>
struct Equal: ::std::binary_function<T, T, bool> {
    bool operator()(T const& left, T const& right) const {
        ::std::equal_to<T> equal;

        return equal(left, right);
    }
};

template<class T>
struct Equal<T*> : ::std::binary_function<T*, T*, bool> {
    bool operator()(T* const & left, T* const & right) const {
        Equal<T> equal;

        return equal(*left, *right);
    }
};

<强> 2。散列:

template<class T>
struct Hash: ::std::unary_function<T, ::std::size_t> {
    ::std::size_t operator()(T const & value) const {
        ::boost::hash<T> hash;

        return hash(value);
    }
};

template<class T>
struct Hash<T*> : ::std::unary_function<T*, ::std::size_t> {
    ::std::size_t operator()(T* const & value) const {
        Hash<T> hash;

        return hash(*value);
    }
};

所以现在我可以继续使用Boost的hash_value并且它不会被Boost的默认实现屏蔽掉指针类型(参见 EDIT 3 )。

第3。例如:

在我的应用程序中,我有一个unordered_set的瘦包装器,现在看起来像这样:

template<class T, class H = Hash<T>, class E = Equal<T> >
class Set {
public:

// code omitted...

    bool contains(const T& element) const {
        return s_.find(element) != end();
    }

    bool insert(const T& element) {
        return s_.insert(element).second;
    }

// code omitted...

private:
    ::boost::unordered::unordered_set<T, H, E> s_;
};

所以,如果我们有一些基类:

class Base {
public:
    Base(const ::std::string& string) {
        if (string.empty())
            throw ::std::invalid_argument("String is empty.");

        string_ = string;
    }

    virtual ~Base() {
    }

    friend bool operator==(const Base& right, const Base& left) {
        return typeid(right) == typeid(left) && right.string_ == left.string_;
    }

    friend bool operator!=(const Base& right, const Base& left) {
        return !(right == left);
    }

    friend ::std::size_t hash_value(Base const& base) {
        ::boost::hash<std::string> hash;

        return hash(base.string_);
    }

    friend ::std::size_t hash_value(Base* const& base) {
        return hash_value(*base);
    }

private:
    ::std::string string_;
};

和一些派生类:

class Derived: public Base {
public:
    Derived(const ::std::string& string) :
            Base(string) {
    }

    virtual ~Derived() {
    }
};

然后我们甚至可以使用多态(这是我的主要目的BTW):

Derived d1("¯\_(ツ)_/¯");
Derived d2("¯\_(ツ)_/¯");

Set<Base*> set;

set.insert(&d1);

assert(set.contains(&d2));

希望这会有所帮助。欢迎任何建议。