【问题标题】:template specialization for all subclasses所有子类的模板特化
【发布时间】:2011-10-01 17:39:05
【问题描述】:

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

特别是,我想为 STL 的哈希 这样做。 hash 被定义为一个空的参数化模板,以及一系列针对特定类型的特化:

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);

【问题讨论】:

  • 如果冲突为什么不使用命名空间?
  • ...这里的解决方案是把所有的派生类都以类似的方式命名,不太理想。

标签: c++ templates subclass template-specialization


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 恐怕在这种特殊情况下你不能使用enable_if
    • jpalecek:为什么不呢?顺便说一句,如果 boost 是这里的答案,它可能对我没有帮助,因为我的工作场所限制了我们可以使用的库,并且 boost::enable_if 不是规定子集的一部分。
    • 那么你可能不得不自己动手,但你应该能够通过使用 SFINAE 得到一些你想要的东西。
    • 此回复实际上并未给出 OP 问题的答案。
    【解决方案3】:

    这是我能做的最好的:

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

    不过,我有点担心我不必将 operator() 设为虚拟。

    【讨论】:

      【解决方案4】:

      我认为这是不可能的,因为基于比匹配类型更复杂的东西来进行模板专业化的方法是 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";
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-07
        相关资源
        最近更新 更多