specialize std :: hash< T>依赖类型 [英] specialize std::hash<T> for dependent types

查看:213
本文介绍了specialize std :: hash< T>依赖类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已定义此模板类结构:

  template< typename T& struct Outer {
struct Inner {/ * ... some stuff ... * /};
};

我想把 Inner unordered_map (实际上,它们不是直接的,而是它们的容器,因此指定hashing对象的方法直接对于 unordered_map 这不是一个好主意),因此我想要专门化 hash 这些项目。



不工作,因为编译器不能匹配实例化 hash 时指定的类型的外部< T> :: Inner

  namespace std {
template< typename T& struct hash< typename Outer< T> :: Inner> {
size_t operator()(typename Outer< T> :: Inner const& obj)
{/ * ... some stuff ... * /}
}
};

有没有人知道这个解决方案?



有很多解决方案。



首先,您可以将Inner类移动到Outer类的外部(如果需要,可以将它们设为好友)。您还可以将其移动到详细命名空间或隐藏它的许多其他方式,具体取决于您的上下文。人们避免这种嵌套的Inner类是很常见的,因为它们可能导致一些问题,像这样和一些旧的编译器甚至有问题接受这样的嵌套类。通常最好的做法是将这些嵌套类移出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>内;
friend class detail :: Outer_Inner< T> ;; // Optional

// ...

};

namespace std {
template< typename T>
struct hash< detail :::Outer_Inner< T> > {
// ..
};
};

另一种解决方案是定义自己的哈希类,你可以给 unordered_set 。像这样:

 模板< 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 / *一些哈希码* /;
};
};

// ...

//示例无序集成员:
std :: unordered_set< Inner,InnerHash> m_set;

};

最后,我还有另一个解决方案,就像第一个解决方案,优点是专门化 std :: hash 类模板。然而,这个解决方案有点复杂,它涉及到包装你的Inner类到一个外部类模板,如下:

  template< ;类型名称T> 
struct InnerWrapper {
typedef typename Outer< T> :: Inner value_type;
value_type data;
};

然后创建专业化 std :: hash<内部抽头< T> > 。这个解决方案的真正优点是对现有的Outer类的实现没有干扰,但在这种情况下创建一个 unordered_map 意味着映射必须包含(直接或间接)InnerWrapper对象,而不是直接存储Inner对象。此外,您应该注意到,这个解决方案可以与第一个解决方案混合使用Inner的一些功能更紧密地集成到Outer在一个嵌套类中实现,并具有更多的公共功能的Inner在外部实现类,从而避免了友谊关系,并允许更紧密的外 - 内集成,同时留下一个干净的面向用户的类来访问Inner的功能。


I have defined this template class structure:

template<typename T> struct Outer {
    struct Inner { /* ...some stuff... */ };
};

I want to put Inner objects into an unordered_map (actually, not directly them but containers of them, so the approach of specifying hashing object directly on template parameters for unordered_map is not a great idea), thus I wanted to specialize the hash class for these items.

This will not work, because the compiler cannot match Outer<T>::Inner with the type specified when instantiating hash:

namespace std {
    template<typename T> struct hash<typename Outer<T>::Inner > {
        size_t operator ()( typename Outer<T>::Inner const & obj )
        { /* ...some stuff... */ }
    };
};

Does anyone know a solution to this?

解决方案

You're obviously right that this will not work because the compiler cannot match a template specialization that is based on such a dependent type (e.g., Inner could be a nested typedef, then how would the compiler be able to tell the difference between that type coming from the nested typedef in Outer, versus from elsewhere? It can't, it's impossible to tell).

There are a number of solutions.

First, you could move the Inner class to the outside of the Outer class (and make them friends, if needed). You can also move it into a "detail" namespace or hide it in a number of other ways depending on your context. It is not uncommon for people to avoid such nested "Inner" classes because they can cause a number of problems like this and some older compilers even have problems accepting such nested classes at all. It's generally better practice to just move those nested classes out of the Outer class. In terms of actual code, you do this:

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> > {
    // ..
  };
};

Another solution is to define your own hashing class that you can give to the unordered_set. Like this:

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;

};

And finally, there is yet another solution I can think of that, like the first solution, has the advantage of specializing the std::hash class template. However, this solution is a bit convoluted, it involves wrapping your Inner class into an outside class template, like this:

template <typename T>
struct InnerWrapper {
  typedef typename Outer<T>::Inner value_type;
  value_type data;
};

and then creating the specialization std::hash< InnerWrapper<T> >. This solution really only has the advantage of being non-intrusive on the existing implementation of the Outer class, but creating an unordered_map in this case means that the map must contain (directly or indirectly) InnerWrapper objects instead of storing the Inner objects directly. Also, you should notice that this solution can be mixed with the first solution by having some of the functionality of Inner that is more tightly integrated with Outer implemented in a nested class, and having the more "public" functionality of Inner implemented in the outside class, thus avoiding the friendship relationship and allowing tighter Outer-Inner integration, while leaving a clean user-facing class to access Inner's functionalities.

这篇关于specialize std :: hash&lt; T&gt;依赖类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆