我已经定义了这个模板类结构:
template<typename T> struct Outer {
struct Inner { /* ...some stuff... */ };
};
我想将Inner
个对象放入unordered_map
(实际上,不是直接将它们放在它们的容器中,因此直接在unordered_map
的模板参数上指定散列对象的方法不是一个好主意)因此我想专门为这些项目hash
类。
这不起作用,因为编译器无法将Outer<T>::Inner
与实例化hash
时指定的类型匹配:
namespace std {
template<typename T> struct hash<typename Outer<T>::Inner > {
size_t operator ()( typename Outer<T>::Inner const & obj )
{ /* ...some stuff... */ }
};
};
有没有人知道这方面的解决方案?
答案 0 :(得分:4)
你显然是对的,因为编译器无法匹配基于这种依赖类型的模板特化(例如,Inner可能是嵌套的typedef,编译器如何能够告诉你)来自Outer中嵌套typedef的那种类型与其他地方之间的区别?它不可能告诉它。
有许多解决方案。
首先,您可以将Inner类移动到Outer类的外部(如果需要,可以将它们变成朋友)。您也可以将其移动到&#34;细节&#34;命名空间或根据您的上下文以其他方式隐藏它。人们避免这种嵌套的内部情况并不常见。类,因为它们可能会导致许多这样的问题,而一些较旧的编译器甚至在接受这样的嵌套类时都会遇到问题。将这些嵌套类移出Outer类通常是更好的做法。就实际代码而言,您可以这样做:
template <typename T>
struct Outer; // forward-decl.
namespace detail {
template <typename T>
struct Outer_Inner {
friend class Outer<T>; // Optional
// ....
};
};
template <typename T>
struct Outer {
typedef detail::Outer_Inner<T> Inner;
friend class detail::Outer_Inner<T>; // Optional
// ...
};
namespace std {
template<typename T>
struct hash< detail::Outer_Inner<T> > {
// ..
};
};
另一种解决方案是定义您可以为unordered_set
提供的哈希类。像这样:
template <typename T>
struct Outer {
struct Inner {
//..
};
struct InnerHash {
typedef Inner argument_type;
typedef std::size_t result_type;
result_type operator()(argument_type const& s) const {
return /* some hashing code */;
};
};
// ...
// An example unordered-set member:
std::unordered_set<Inner, InnerHash> m_set;
};
最后,还有另一个我能想到的解决方案,就像第一个解决方案一样,它具有专门化std::hash
类模板的优势。但是,这个解决方案有点复杂,它涉及将Inner类包装到外部类模板中,如下所示:
template <typename T>
struct InnerWrapper {
typedef typename Outer<T>::Inner value_type;
value_type data;
};
然后创建专门化std::hash< InnerWrapper<T> >
。这个解决方案实际上只具有对外类的现有实现非侵入性的优点,但在这种情况下创建unordered_map
意味着映射必须包含(直接或间接)InnerWrapper对象而不是存储内部对象直接。此外,您应该注意到,此解决方案可以与第一个解决方案混合使用,具有与嵌套类中实现的Outer更紧密集成的Inner的一些功能,并且具有更多&#34; public&#34; Inner的功能在外部类中实现,从而避免友谊关系并允许更紧密的外部内部集成,同时留下一个干净的面向用户的类来访问内部功能。