Specialization of STL class template, default of type formal parameters, numerical template parameters and template skills

1. Global specialization

1. All kinds of specialization

  • Which type is difficult to use, we should specialize in which type
  • Making a full class specialization of the class template is equivalent to rewriting a class, and this class is specifically for the previously difficult type

2. Membership specialization

Which member function is difficult to use, we can specialize which member function

#include <iostream>
#include <cstring>
using namespace std;


// Therefore, the add function in this class is not suitable for const char * type, because the pointer cannot add
template<class T>
class CMath{
public:
    CMath(T const& t1, T const t2):m_t1(t1), m_t2(t2){}

    T add(void){
            return m_t1 + m_t2;
    }
private:
    T m_t1;
    T m_t2;
};

// Full class specialization is equivalent to rewriting a class for a specific type (char* const)
/*
template<>
class CMath<char* const>{
public:
    CMath(char* const& t1, char* const& t2):m_t1(t1), m_t2(t2){}

    char* const add(void){
            return strcat(m_t1, m_t2);
    }
private:
    char* const m_t1;
    char* const m_t2;
};
*/

// It's troublesome because only the add function is not suitable for char* const type. Just rewrite the add function
// All have member specialization
template<>
char* const CMath<char* const>::add(void){
        return strcat(m_t1, m_t2);
}

int main(void){
    CMath<int> m1(1, 1);
    std::cout << m1.add() << std::endl; // 2
    // The character array name is char* const
    char c_x[256] = "hello ", c_y[256] = "world!";
    // When cmath < char * const >, you won't use the class template to instantiate the class, but directly use the specialized class
    CMath<char* const>m2(c_x, c_y);
    cout << m2.add() << endl;           // hello world!
    return 0;
}

2. Local specialization

  • The difference from global specialization is that several type parameters are specialized. If all type parameters are specialized into specific types, it is global specialization. If only some type parameters are specialized, it is local specialization.
#include <iostream>
using namespace std;

// Class template
template <class T, class D>
class A{
public:
    // static is written here so that it can be called directly through a class without instantiating an object
    static void foo(){
        cout << "1. A<T, D>::foo()" << endl;
    }
};

// Local specialization, specialization D into short
template <class T>
class A<T, short>{
public:
    static void foo(){
        cout << "2. A<T, short>::foo()" << endl;
    }
};

// Local specialization, specialization D into T
template <class T>
class A<T, T>{
public:
    static void foo(){
        cout << "3. A<T, T>::foo()" << endl;
    }
};

int main(void){
    A<int, double>::foo();  // 1. A<T, D>::foo()
    A<int , short>::foo();  // 2. A<T, short>::foo()
    // A<short, short>::foo(); // Compilation reports an error, resulting in ambiguity between 2 and 3
    return 0;
}

3. Type parameter default

#include <iostream>
#include <typeinfo>
using namespace std;

// Class template
template <class T = short, class D = double>
class A{
public:
    void print(void){
        cout << "m_t: " << typeid(m_t).name() << ","
             << "m_d: " << typeid(m_d).name() << endl;
    }
private:
    T m_t;
    D m_d;
};

int main(void){
    A<int, float> m1;
    m1.print(); // m_t: i,m_d: f
    A<>m2;
    m2.print(); // m_t: s,m_d: d
}

4. Template parameters of numerical shape

  • Type parameters can appear in the type parameter table. Numerical parameters can also have default values
  • Can't appear string, double this kind of type, can only appear numeric parameter, can only appear int type?
  • size_t is long unsigned int
#include <iostream>
using namespace std;

// Type parameters can appear in the type parameter table. Numerical parameters can also have default values
template<class T, size_t S =10>
class Array{
public:
    // operators overloading
    T& operator[](size_t i){
        return m_arr[i];
    }
    size_t size(){
        return S;
    }
private:
    T m_arr[S];
};

int main(void){
    // Array<int> a;  A can be regarded as a one-dimensional array class, and each element in the array is of type int
    // m can be regarded as a one-dimensional array. Each element in the array is of type array < int > and is a one-dimensional array
    // So m is a two-dimensional array
    Array<Array<int, 3> ,3> m;   // The last two < are written separately to avoid ambiguity
    for (int i=0; i<m.size(); i++){
        for (int j=0; j<m.size(); j++){
            m[i][j] = 1;
        }
    }
    for (int i=0; i<m.size(); i++){
        for (int j=0; j<m.size(); j++){
            cout << m[i][j] << " ";
        }
        cout << endl;
    }    
    return 0;
}
$ ./a.out 
1 1 1 
1 1 1 
1 1 1

5. Template skills

Personally, I don't think the latter two are common, but now that I see them, I'll write about them and leave a seed. In case I encounter them in future development, it's also easy to expand.

5.1 template member variables

  • m_s is first a member variable, and then an unknown class instantiated by the class template array, because D is unknown
#include <iostream>
using namespace std;

template<class T>
class Array{
public:
    // operators overloading
    T& operator[](size_t i){
        return m_arr[i];
    }
private:
    T m_arr[10];
};

template <class D> class Sum{ 
public:
    Sum(Array<D>& s):m_s(s){}
    D add(){     //Sum
        D d = 0;
        for(int i = 0; i < 10; i++){
            d += m_s[i];
        }
        return d;
    }
private:
    Array<D> m_s; // Template member variable
};

int main(void){
    Array<int> a;
    for (int i = 0; i < 10; i++){
        a[i] = i + 1;
    }
    // Sum class template instantiates a class sum < int > to instantiate an object s, and uses a to assign the initial value
    Sum<int> s(a);
    cout << s.add()  << endl; //55
    return 0;
}

5.2 template member function

Member function template of class template

  • foo is a function template and a member function, which can be directly called member function template
  • To put it bluntly, a member function template is nested in the class template. When using, you need to instantiate the class first and then the function
#include <iostream>
using namespace std;
template <class T>
class A{
public:
    // The declaration definition is written together
    template<class D> void foo(){       //Member function template
        cout << "A<T>::foo()" << endl;
    }
    // Declaration definitions are written separately
    template<class X> void foo2();
};

// Since it is template nesting, there are two hats. There should also be scope restrictions
template<class T>
template<class X> 
void A<T>::foo2(){
    cout << "A<T>::foo2()" << endl;
}

int main(void){
    // 1. Instantiation class
    A<int> m;
    // 2. Instantiation class
    m.foo<double>();    // A<T>::foo()

    m.foo2<double>();   // A<T>::foo2()

    return 0;
}

5.3 template member type

#include <iostream>
using namespace std;

// Class template nested class template
template <class X>class A{
public:
    template <class Y>class B{
    public:
        template <class Z>class C;
    };
};

// Class template C is implemented externally
template <class X>
template <class Y>
template <class Z> class A<X>::B<Y>::C{ // Turn over the scope twice
public:
    // Here is another member function template, which needs to be instantiated
    template<class T>void foo(){
        cout << "zhen e xin" << endl;
    }
};

int main(void){
    A<int>::B<int>::C<int> a;
    a.foo<int>();   // "zhen e xin"
    return 0;
}

5.4 template parameters

  • The template parameters in < > can be type, value or class template
  • In the figure above, array is a class template whose template parameter is T
  • Sum is also a class template. Its template parameter is C and its type is a class template: template < class D > class C. the default value is class template array. This is the template type template parameter
#include <iostream>
using namespace std;

template<class T>
class Array{
public:
    // operators overloading
    T& operator[](size_t i){
        return m_arr[i];
    }
private:
    T m_arr[10];
};


template <class D, template<class M> class C> class Sum{ 
public:
    // Sum(Array<D>& s):m_s(s){}
    Sum(C<D>& s):m_s(s){}
    D add(){     //Sum
        D d = 0;
        for(int i = 0; i < 10; i++){
            d += m_s[i];
        }
        return d;
    }
private:
    // Array<D> m_s;
    C<D> m_s; // Template member variable
};

int main(void){
    Array<int> a;
    for (int i = 0; i < 10; i++){
        a[i] = i + 1;
    }
    // Sum class template instantiates a class sum < int > to instantiate an object s, and uses a to assign the initial value
    // Sum<int> s(a);
    Sum<int, Array> s(a);
    cout << s.add()  << endl; //55
    return 0;
}

Tags: STL

Posted by LankyMac on Sat, 16 Apr 2022 20:25:53 +0930