所有子类的模板特化

时间:2011-10-01 17:39:06

标签: c++ subclass template-specialization

我想定义一个适用于给定基类的所有子类的C ++模板特化。这可能吗?

特别是,我想为STL的哈希<>执行此操作。散列<>被定义为一个空的参数化模板,以及一系列特定类型的特化:

template<class _Key>
  struct hash { };

template<>
  struct hash<char>
  {
    size_t
    operator()(char __x) const
    { return __x; }
  };

template<>
  struct hash<int>
  {
    size_t
    operator()(int __x) const
    { return __x; }
  };
...

我想定义这样的内容:

template<class Base>
  struct hash {
    size_t operator()(const Base& b) const {
      return b.my_hash();
    }
  };

class Sub : public Base {
  public:
    size_t my_hash() const { ... }
};

并能够像这样使用它:

hash_multiset<Sub> set_of_sub;
set_of_sub.insert(sub);

但是,我的哈希模板与STL中的通用模板冲突。是否有一种方法(可能使用特征)来定义适用于给定基类的所有子类的模板特化(不修改STL定义)?

请注意,我知道只要需要这个哈希特化,我就可以使用一些额外的模板参数来执行此操作,但是如果可能的话我想避免这种情况:

template<>
  struct hash<Base> {
    size_t operator()(const Base& b) const {
      return b.my_hash();
    }
  };

....

// similar specialization of equal_to is needed here... I'm glossing over that...
hash_multiset<Sub, hash<Base>, equal_to<Base> > set_of_sub;
set_of_sub.insert(sub);

4 个答案:

答案 0 :(得分:1)

解决方案是使用SFINAE来决定是否允许根据类继承结构进行专业化。在Boost中,您可以使用enable_ifis_base_of来实现此目的。

答案 1 :(得分:0)

这是我能做的最好的事情:

template<>
  struct hash<Sub> : hash<Base> {
  };

我有点担心我不需要虚拟operator()

答案 2 :(得分:0)

C ++ 11 起,您可以将SFINAE与标准库enable_ifis_base_of一起使用来解决问题。

答案 3 :(得分:0)

我认为这是不可能的,因为基于比仅匹配类型更复杂的东西进行模板特化的方法是 C++ SFINAE,它需要第二个(虚拟)模板参数。不幸的是,std::hash 只接受一个模板参数,并且不允许使用两个模板参数创建另一个版本的 std::hash。因此,如果您对 Jayen's solution 不满意,您可以创建自己的 hash 类型:

#include <iostream>
#include <type_traits>

using namespace std;

class ParentClass {};
class ChildClass : public ParentClass {};

// SFINAE, if T is not a child of ParentClass, substitution will fail
// You can create specializations for the general case, for another base classes, etc.
template<typename T, typename=typename enable_if<is_base_of<ParentClass, T>::value, T>::type>
struct your_hash
{
    size_t operator()(const T& value)
    {
        return 42;
    }
};

int main()
{
  ParentClass pc;
  ChildClass cc;
  cout<<your_hash<ParentClass>()(pc)<<"\n";
  cout<<your_hash<ChildClass>()(cc)<<"\n";
}