9
votes

When a template is fully specialized, a member function does not need to be duplicated. For example, in the following code, foo() is written only once.

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

However, for partial specialization, it is necessary to duplicate the class definition and all member functions. For example:

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

Notice that A<2, N>::foo() a duplicate of A<M, N>::foo() with 2 manually substituted for M.

How can such code duplication be avoided in this context of template partial specialization?

4
I had no idea you can specialize a method for a class template without specializing the whole class.bolov

4 Answers

2
votes

In this case I would make a base class not knowing the template parameter '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();
}

Alternatively, you could specialize the entire base class.

1
votes

You could move header into a separate class and only partial specialize this one:

#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

live example

1
votes

Obligatory answer using tag dispatch:

You can create an overloaded helper function; one that gets called in the M == 2 case, and the other when M != 2. This allows you to avoid creating a templated base class. All we need to do is turn the condition M == 2 into a type, which we'll do using std::true_type and std::false_type from <type_traits>

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

To perform the translation into a type (compile-time check):

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

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

And you can call them like so:

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


If you want to avoid needing to create a concept, then you can instead overload on std::integral_constant, but you will get some compile-time template bloat (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

0
votes

Thanks to all those who have provided answers.

Following the link provided by Vaughn Cato and continuing along another link leads to this solution, which uses std::enable_if rather than template partial specialization.

Implementing it for the problem at hand gives:

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