【问题标题】:template partial specialization: How can code duplication be avoided?模板偏特化:如何避免代码重复?
【发布时间】:2016-01-25 17:21:37
【问题描述】:

当模板完全特化时,不需要复制成员函数。例如,在下面的代码中,foo() 只写了一次。

#include <iostream>

template<int M>   
class B
{              
public:
    void foo();   
private:
    void header();
};         

template<int M>   
void          
B<M>::foo()
{
    // specialized code:              
    header();
    // generic code:
    std::cout << "M = " << M << std::endl;             
}                   

template<int M>                                                             
void                                                                        
B<M>::header()                                                              
{                                                                           
    std::cout << "general foo()" << std::endl;                              
}                                                                           

template<>                                                                  
void                                                                        
B<2>::header()                                                              
{                                                                           
    std::cout << "special foo()" << std::endl;
}

但是,对于部分特化,有必要复制类定义和所有成员函数。例如:

#include <iostream>

template<int M, int N>
class A
{                  
public:   
    void foo();   
private:
    void header();
};     

template<int M, int N>
void              
A<M, N>::foo()
{          
    // specialized code:
    header(); 
    // generic code:
    std::cout << "M = " << M << ", N = " << N << std::endl;
}                                     

template<int M, int N>
void                                                   
A<M, N>::header()   
{                                                                           
    std::cout << "general foo()" << std::endl;                              
}                                                                           

template<int N>                                                             
class A<2, N>                                                               
{                                                                           
public:                                                                     
    void foo();                                                             
private:                                                                    
    void header();                                                          
};                                                                          

template<int N>                                                             
void                                                                        
A<2, N>::foo()                                                              
{                                                                           
    // specialized code:                                                    
    header();                                                               
    // generic code:                                                        
    std::cout << "M = " << 2 << ", N = " << N << std::endl;                 
}                                                                           

template<int N>
void                                                                        
A<2, N>::header()                                                           
{                                                                           
    std::cout << "special foo()" << std::endl;                              
}

请注意,A&lt;2, N&gt;::foo()A&lt;M, N&gt;::foo() 重复,其中 2 手动替换了 M

在模板偏特化的情况下,如何避免重复代码?

【问题讨论】:

标签: c++ templates partial-specialization


【解决方案1】:

在这种情况下,我会创建一个不知道模板参数“N”的基类:

#include <iostream>

template<int M>
class ABase
{
protected:
    void header();
};

template<int M>
void
ABase<M>::header()
{
    std::cout << "general header()" << std::endl;
}


template<>
void ABase<2>::header()
{
    std::cout << "special header()" << std::endl;
}

template<int M, int N>
class A : private ABase<M>
{
public:
    void foo();
};

template<int M, int N>
void
A<M, N>::foo()
{
    // specialized code:
    this->header();
    // generic code:
    std::cout << "M = " << M << ", N = " << N << std::endl;
}

int main()
{
    A<1,0> a1;
    a1.foo();

    A<2,0> a2;
    a2.foo();
}

或者,您可以专门化整个基类。

【讨论】:

    【解决方案2】:

    您可以将header 移动到一个单独的类中,并且只对这个类进行部分特化:

    #include <iostream>
    
    template <int M, int N>
    struct Header
    {
        static void header()
        {
            std::cout << "general foo()" << std::endl;
        }
    };
    
    template <int N>
    struct Header<2, N>
    {
        static void header()
        {
            std::cout << "special foo()" << std::endl;
        }
    };
    
    template<int M, int N>
    struct A
    {                  
        void foo();
    };     
    
    template<int M, int N>
    void              
    A<M, N>::foo()
    {          
        Header<M,N>::header(); 
        std::cout << "M = " << M << ", N = " << N << std::endl;
    }
    
    int main()
    {
        A<1,1> a11;
        a11.foo();
    
        A<2,5> a25;
        a25.foo();
    }
    

    输出

    general foo()
    M = 1, N = 1
    
    special foo()
    M = 2, N = 5
    

    live example

    【讨论】:

      【解决方案3】:

      使用标签调度的强制性回答:

      你可以创建一个重载的辅助函数;一个在M == 2 的情况下被调用,另一个在M != 2 的情况下被调用。这使您可以避免创建模板化基类。我们需要做的就是将条件M == 2 转换为类型,我们将使用std::true_typestd::false_type 中的&lt;type_traits&gt; 来实现这一点

      template<int M, int N>
      class A
      {                  
      public:   
          void foo();   
      private:
          void header();
          void foo_helper(std::true_type); // for M == 2 case
          void foo_helper(std::false_type); // for M != 2 case
      };
      

      执行转换为类型(编译时检查):

      template<int I>
      struct is_2 : std::false_type{};
      
      template<>
      struct is_2<2> : std::true_type{};
      

      你可以这样称呼他们:

      template<int M, int N>                                                             
      void                                                                        
      A<M, N>::foo()                                                              
      {       
          foo_helper(typename is_2<M>::type{});
          // specialized code:                                                    
          header();                                                               
          // generic code:                                                        
          std::cout << "M = " << M << ", N = " << N << std::endl;                 
      } 
      
      template<int M, int N>                                                             
      void                                                                        
      A<M, N>::foo_helper(std::true_type)
      {
          std::cout << "Specialized code for M==2 case\n";
      }
      
      template<int M, int N>                                                             
      void  
      A<M,N>::foo_helper(std::false_type)
      {
          std::cout << "M!=2 case\n";
      }
      

      Demo


      如果你想避免创建一个概念,那么你可以重载std::integral_constant,但你会得到一些编译时模板膨胀(See Jarod42's answer here):

      // inside void foo()
      foo_helper(std::integral_constant<int, M>());
      
      
      template<typename T>
      void foo_helper(T) // for M != 2 case
      {
          std::cout << "M!=2 case\n";
      }
      
      
      void foo_helper(std::integral_constant<int, 2>) // for M == 2 case  
      {
          std::cout << "Specialized code for M==2 case\n";
      }
      

      Demo 2

      【讨论】:

        【解决方案4】:

        感谢所有提供答案的人。

        跟随Vaughn Cato 提供的链接并沿着另一个链接继续会导致this 解决方案,它使用std::enable_if 而不是模板部分特化。

        针对手头的问题实施它会给出:

        #include <iostream>
        
        template<int M, int N>
        class A
        {
        public:
            void foo();
        
        private:
            template<int MM = M, int NN = N,
                    typename std::enable_if<MM != 2>::type* = nullptr>
            void header()
            {
                std::cout << "general foo()" << std::endl;
            }
        
            template<int MM = M, int NN = N,
                    typename std::enable_if<MM == 2>::type* = nullptr>
            void header()
            {
                std::cout << "special foo()" << std::endl;
            }
        };
        
        template<int M, int N>
        void
        A<M, N>::foo()
        {
            // specialized code:
            header();
            // generic code:
            std::cout << "M = " << M << ", N = " << N << std::endl;
        }
        

        【讨论】:

          猜你喜欢
          • 2017-09-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-29
          • 1970-01-01
          • 1970-01-01
          • 2021-11-07
          相关资源
          最近更新 更多