代码之家  ›  专栏  ›  技术社区  ›  Doug

如何为所有派生类型部分专用化类模板?

  •  8
  • Doug  · 技术社区  · 15 年前

    我想部分专用化一个我无法更改的现有模板( std::tr1::hash )对于基类和所有派生类。原因是我正在使用奇怪的重复模板模式来实现多态性,散列函数是在crtp基类中实现的。如果我只想部分专门化一个crtp基类,那么很容易,我可以编写:

    
    namespace std { namespace tr1 {
    
    template <typename Derived>
    struct hash<CRTPBase<Derived> >
    {
        size_t operator()(const CRTPBase<Derived> & base) const 
        { 
            return base.hash(); 
        }
    };
    
    } }
    

    但是这种专门化并不匹配实际的派生类,只是 CRTPBase<Derived> . 我想要的是一种为 Derived 如果且仅当其来源于 crtpbase派生的 . 我的伪代码是

    
    namespace std { namespace tr1 {
    
    template <typename Derived>
    struct hash<typename boost::enable_if<std::tr1::is_base_of<CRTPBase<Derived>, Derived>,
        Derived>::type>
    {
        size_t operator()(const CRTPBase<Derived> & base) const 
        { 
            return base.hash(); 
        }
    };
    
    } }
    

    …但这不起作用,因为编译器无法分辨 enable_if<condition, Derived>::type 衍生的 . 如果我能改变的话 STD::Tr1::哈希 ,我只需添加另一个要使用的虚拟模板参数。 boost::enable_if ,按照 enable_if 文档,但这显然不是一个很好的解决方案。有办法解决这个问题吗?是否必须在每个 unordered_set unordered_map 我创建或完全专业化 hash 对于每个派生类?

    2 回复  |  直到 15 年前
        1
  •  8
  •   Alexey Malistov    15 年前

    以下代码有两种变体。你可以选择更适合你的。

    
    template <typename Derived>
    struct CRTPBase
    {
        size_t hash() const {return 0; }
    };
    
    // First case 
    //
    // Help classes
    struct DummyF1 {};
    struct DummyF2 {};
    struct DummyF3 {};
    template<typename T> struct X; 
    
    // Main classes
    template<> struct X<DummyF1> : CRTPBase< X<DummyF1> > {
        int a1;
    };
    
    template<> struct X<DummyF2> : CRTPBase< X<DummyF2> > {
        int b1;
    };
    
    // typedefs
    typedef X<DummyF1> F1;
    typedef X<DummyF2> F2;
    typedef DummyF3    F3; // Does not work
    
    namespace std { namespace tr1 {
        template<class T>
        struct hash< X<T> > {
            size_t operator()(const CRTPBase< X<T> > & base) const     
            {         
                return base.hash();     
            }
        };
    }} // namespace tr1 // namespace std 
    
    //
    
    // Second case
    struct DummyS1 : CRTPBase <DummyS1> {
        int m1;
    };
    //
    template<typename T> 
    struct Y : T {};
    //
    typedef Y<DummyS1> S1;
    
    
    namespace std { namespace tr1 {
        template<class T>
        struct hash< Y<T> > {
            size_t operator()(const CRTPBase<T> & base) const     
            {         
                return base.hash();     
            }
        };
    }} // namespace tr1 // namespace std 
    
    void main1()
    {
        using std::tr1::hash;
        F1 f1;
        F2 f2;
        F3 f3;
        hash<F1> hf1; size_t v1 = hf1(f1); // custom hash functor
        hash<F2> hf2; size_t v2 = hf2(f2); // custom hash functor
        hash<F3> hf3; size_t v3 = hf3(f3); // error: standard hash functor
    
        S1 s1;
        hash<S1> hs1; size_t w1 = hs1(s1); // custom hash functor
    
    }
    
        2
  •  1
  •   Alexey Malistov    15 年前

    而不是修改 std::tr1::hash 您应该创建自己的名称空间并在其中定义新的结构 hash 继承自 STD::Tr1::哈希 或专门用于 CRTPBase<Derived> .

    
    template <typename Derived>
    struct CRTPBase
    {
        size_t hash() {return 0; }
    };
    
    struct AA : CRTPBase <AA> {};
    struct BB {};
    //
    namespace mynamespace {
    
    template <typename Some, typename Dummy=char> 
    struct hash : std::tr1::hash<Some> {};
    //
    template <typename Derived>
    struct hash<Derived, 
      typename boost::enable_if< std::tr1::is_base_of<CRTPBase<Derived>, Derived>, char>::type >
    {    
        size_t operator()(const CRTPBase<Derived> & base) const     
        {         
            return base.hash();     
        }
    };
    
    } // namespace mynamespace {}
    //
    //
    void ff()
    {
        using namespace mynamespace;
    
        hash<AA> aa;  // my hash
        hash<BB> bb;  // std::tr1::hash
    
    }