Detailed explanation of C++ template specialization and partial specialization

  • 2020-04-02 02:48:43
  • OfStack

preface

Speaking of C++ templates, this is not new and has been used in actual development; For C++ template specialization and partial specialization, for others, is not a new thing, but for me, it is really my blind spot, that day in the group to discuss this problem, I do not grasp this part, and associate with the "STL source code analysis" book, for this is also an introduction. So, today, we will summarize this in detail for later forgetting.

C + + template

When it comes to C++ template specialization and partial specialization, we have to briefly talk about C++ templates. As we all know, strongly typed programming forces us to write consistent code for objects with the same logical structure but different data types, rather than extracting the commonalities, which is obviously not conducive to program expansion and maintenance. C++ templates came into being. The C++ templates provide a definition of the generic behavior of data objects with the same logical structure. The type of these template operands is not an actual data type, but a parameterized type. Templates in C++ are divided into class templates and function templates.

Class template is as follows:


#include <iostream>
using namespace std;
 
template <class T>
class TClass
{
public:
     //The member function of TClass is
 
private:
     T DateMember;
};

The function template is as follows:


template <class T>
T Max(const T a, const T b)
{
     return  a > b ? a : b;
}

A template specialization

Sometimes a template needs to be specialized for a particular type, which is called special processing. For example, there is the following code:


#include <iostream>
using namespace std;
 
template <class T>
class TClass
{
public:
     bool Equal(const T& arg, const T& arg1);
};
 
template <class T>
bool TClass<T>::Equal(const T& arg, const T& arg1)
{
     return (arg == arg1);
}
 
int main()
{
     TClass<int> obj;
     cout<<obj.Equal(2, 2)<<endl;
     cout<<obj.Equal(2, 4)<<endl;
}

Class contains an Equal method to compare the equality of two arguments; The above code runs without any problems; However, have you ever thought that in real development, you should never write this way, and you should never use the "==" symbol to judge the parameters of float or double? So, for float or double, we need a special treatment, which is as follows:


#include <iostream>
using namespace std;
 
template <class T>
class Compare
{
public:
     bool IsEqual(const T& arg, const T& arg1);
};
 
//It no longer has the meaning of template, but is now explicitly float
template <>
class Compare<float>
{
public:
     bool IsEqual(const float& arg, const float& arg1);
};
 
//It no longer has the meaning of template, but is explicitly double
template <>
class Compare<double>
{
public:
     bool IsEqual(const double& arg, const double& arg1);
};
 
template <class T>
bool Compare<T>::IsEqual(const T& arg, const T& arg1)
{
     cout<<"Call Compare<T>::IsEqual"<<endl;
     return (arg == arg1);
}
 
bool Compare<float>::IsEqual(const float& arg, const float& arg1)
{
     cout<<"Call Compare<float>::IsEqual"<<endl;
     return (abs(arg - arg1) < 10e-3);
}
 
bool Compare<double>::IsEqual(const double& arg, const double& arg1)
{
     cout<<"Call Compare<double>::IsEqual"<<endl;
     return (abs(arg - arg1) < 10e-6);
}
 
int main()
{
     Compare<int> obj;
     Compare<float> obj1;
     Compare<double> obj2;
     cout<<obj.IsEqual(2, 2)<<endl;
     cout<<obj1.IsEqual(2.003, 2.002)<<endl;
     cout<<obj2.IsEqual(3.000002, 3.0000021)<<endl;
}

Template specialization

The above is a summary of the specialization of templates. What about the partial specialization of the template? The so-called partial specialization refers to provide another copy of the template definition, and its itself is still a templatized; That is, a specialized version of the template parameter designed for further conditional constraints. This kind of partial specialization is widely used in STL. Such as:


template <class _Iterator>
struct iterator_traits
{
     typedef typename _Iterator::iterator_category iterator_category;
     typedef typename _Iterator::value_type        value_type;
     typedef typename _Iterator::difference_type   difference_type;
     typedef typename _Iterator::pointer           pointer;
     typedef typename _Iterator::reference         reference;
};
 
// specialize for _Tp*
template <class _Tp>
struct iterator_traits<_Tp*>
{
     typedef random_access_iterator_tag iterator_category;
     typedef _Tp                         value_type;
     typedef ptrdiff_t                   difference_type;
     typedef _Tp*                        pointer;
     typedef _Tp&                        reference;
};
 
// specialize for const _Tp*
template <class _Tp>
struct iterator_traits<const _Tp*>
{
     typedef random_access_iterator_tag iterator_category;
     typedef _Tp                         value_type;
     typedef ptrdiff_t                   difference_type;
     typedef const _Tp*                  pointer;
     typedef const _Tp&                  reference;
};

See? This is the template partial specialization, and the difference between template specialization, template specialization, its itself is not actually templatized, and partial specialization, still with a templatized. Let's look at a practical example:


#include <iostream>
using namespace std;
 
//General design
template <class T, class T1>
class TestClass
{
public:
     TestClass()
     {
          cout<<"T, T1"<<endl;
     }
};
 
//
for the ordinary pointer partial design template <class T, class T1>
class TestClass<T*, T1*>
{
public:
     TestClass()
     {
          cout<<"T*, T1*"<<endl;
     }
};
 
//Partial design for const pointer
template <class T, class T1>
class TestClass<const T*, T1*>
{
public:
     TestClass()
     {
          cout<<"const T*, T1*"<<endl;
     }
};
 
int main()
{
     TestClass<int, char> obj;
     TestClass<int *, char *> obj1;
     TestClass<const int *, char *> obj2;
 
     return 0;
}

I'm not going to write the output here, but you can try it out.

The call order of specialization and partial specialization

Given the existence of templates, template specialization, and partial specialization, how does the compiler choose when matching at compile time? From a philosophical point of view, the most special should be taken care of first, then the least special, and finally the most common. The compiler makes choices based on this principle. From the above example, we can also see that, so I won't give you an example.

conclusion

My understanding of template specialization and partial specialization may not be correct. I want you to talk to me. I'm just summarizing some of my understanding here. Finally, I hope you will give me some pertinent Suggestions for my blog. I believe that sharing makes us better.


Related articles: