模板偏特化:如何避免代码重复?

9
当模板被完全特化时,成员函数就不需要被复制了。例如,在下面的代码中,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<2, N>::foo()是手动替换了M为2的A<M, N>::foo()的副本。
在模板部分特化的上下文中,如何避免这种代码重复呢?

我不知道你可以为类模板专门化一个方法而不专门化整个类。 - bolov
1
相关链接:https://dev59.com/QYLba4cB1Zd3GeqPaihb - Vaughn Cato
4个回答

2
在这种情况下,我会创建一个基类,不知道模板参数“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();
}

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


1
您可以将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();
}

output

general foo()
M = 1, N = 1

special foo()
M = 2, N = 5

现场示例


1

使用标签分发的必要答案:

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

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";
}

演示


如果你想避免需要创建一个概念,那么你可以选择在std::integral_constant上进行重载,但是你会得到一些编译时模板膨胀(参见Jarod42的答案):

// 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";
}

演示2


0
感谢所有提供答案的人。
通过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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接