缩小范围:我目前正在使用Boost.Unordered。我看到两种可能的解决方案:
定义我自己的Equality Predicates and Hash Functions并利用模板(可能is_pointer
)区分指针和实例;
只需通过提供boost::hash
来扩展hash_value(Type* const& x)
,就像哈希一样;并使用==
参数将(Type* const& x, Type* const& y)
运算符重载作为自由函数添加,以进行相等性检查。
我不确定这两种变化是否真的可行,因为我没有测试它们。我想找出你处理这个问题。欢迎实施:)
编辑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。
答案 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));
希望这会有所帮助。欢迎任何建议。