Qt&C++ Technical Analysis 1 – Class Template Specialization

Table of Contents

    • class template specialization
      • class template specialization
        • full specialization
        • partial specialization
        • specialized template
      • Traits Technology
      • Type classification
      • Reduce code bloat

Class template specialization

Class template specialization

The following is a standard class template that does not use any template specialization techniques
So no matter what type the template parameter T is, Stack has the same behavior

template <typename T>
class Stack {<!-- -->
private:
    std::vector<T> elems;
...
}

There are two forms of class template specialization:

  1. full class template specialization
  2. partial class template specialization

Full specialization

Definition: Refers to providing a single implementation for a specific type when all template parameters are determined

The code below indicates that when the template parameter is a specific std::string, use all the codes after the sequence number 2 to instantiate the class

full specialization requirements

  • template content should be empty
  • class defines the data types that require specialization
  • Number three fills in all the operations that need to be performed on the specialized data type
template<> ①
class Stack<std::string> {<!-- --> ②
private:
    std::deque<std::string> elems; ③
public:
    void push(std::string const & amp; elem) {<!-- -->
        elems.push_back(elem);
    }
    void pop(){<!-- -->
        if (elems.empty()) return;
        elems. pop_back();
    }
    std::string top() const{<!-- -->
        if (elems.empty()) return NULL;
        return elems.back();
    }
}

Partial specialization

Definition: Provide a single implementation for a specific type when a part of the template parameters are determined

The following code means that when the template parameters we pass in T all have T* characteristics, use the following code to perform class instantiation

template <typename T> ①
class Stack<T*> {<!-- --> ②
private:
    std::list<T*> list; ③
public:
    void push(T* & amp; elem) {<!-- -->
    list.push_front(elem);
    }
    void pop(){<!-- -->
    if (list.empty()) return;
    list. pop_front();
    }
    T* top() const{<!-- -->
    if (list.empty()) return NULL;
    return list. front();
    }
}

Specialized template

Suppose there is currently a main class template

template <typename T1, typename T2>
class MyClass {<!-- -->
…
};

Then the corresponding specialized class template can be written like this
Indicates that the received second template parameter T2 must be of type int

template <typename T>
class MyClass<T,int> {<!-- -->
…
};

For example, there are some specializations, you can refer to the following code

template <typename T1, typename T2>
class MyClass<T1*,T2*> {<!-- -->
…
};

Traits Technology

Definition: The Traits technique is a programming technique used in object-oriented programming to achieve code reuse and composability. Traits technology allows programmers to define reusable components that can be used by multiple classes or objects and can be combined at runtime

Here’s a simple case of using the traits technique

// Declare a template class fp_traits to implement the characteristics of the floating point type
template <typename numT>
struct fp_traits {<!-- --> };

// Template specialization, specifying fp_traits of type float
template<>
struct fp_traits<float> {<!-- -->
    typedef float fp_type; // define typedef as float type
    enum {<!-- --> max_exponent = FLT_MAX_EXP }; // Define the enumeration constant, representing the maximum exponent of float type
    static inline fp_type epsilon() // Define the inline function and return the minimum value of type float
    {<!-- --> return FLT_EPSILON; }
};

// Template specialization, specifying fp_traits of type double
template<>
struct fp_traits<double> {<!-- -->
    typedef double fp_type; // define typedef as double type
    enum {<!-- --> max_exponent = DBL_MAX_EXP }; // Define the enumeration constant, representing the maximum exponent of double type
    static inline fp_type epsilon() // Define the inline function and return the minimum value of type double
    {<!-- --> return DBL_EPSILON; }
};

// Declare the template class matrix, and use the numT template parameter to represent the numeric type in the matrix
template <typename numT>
class matrix {<!-- -->
public:
    typedef numT num_type; // define typedef as numT type
    typedef fp_traits<num_type> num_type_info; // define typedef as fp_traits<num_type> type
    // Define the inline function and return the result of the epsilon() function in num_type_info
    inline num_type epsilon()
    {<!-- -->return num_type_info::epsilon();}
    ?…
};

int main()
{<!-- -->
    matrix <float> fm; // define a matrix object of type float
    matrix <double> dm; // define a matrix object of double type
    cout << "float matrix: " << fm.epsilon() << endl; // Output the result of the epsilon() function of the float type matrix object
    cout << "double matrix: " << dm.epsilon() << endl; // Output the result of the epsilon() function of the double type matrix object
}

Type classification

Definition: Since cpp comes with few type processing methods, the method of using class template specialization technology to design a special class template to provide the required information is called type classification technology

// type template, used to provide type information for the specified type T
template<typename T>
class TypeInfo {<!-- -->
public:
    enum {<!-- --> IsPtrT = 0, IsRefT = 0, IsArrayT = 0 }; // Define three enumeration constants, indicating that T is not a pointer, not a reference, not an array
    typedef T baseT; // define typedef as T type
    typedef T bottomT; // define typedef as T type
};

// Type template specialization to provide type information for pointer type T*
template<typename T>
class TypeInfo<T*> {<!-- -->
public:
    enum {<!-- --> IsPtrT = 1, IsRefT = 0, IsArrayT = 0 }; // Define three enumeration constants, indicating that T* is a pointer, not a reference, not an array
    typedef T baseT; // define typedef as T type
    typedef typename TypeInfo<T>::bottomT bottomT; // define typedef as TypeInfo<T>::bottomT type
};

// Type template specialization to provide type information for reference type T & amp;
template<typename T>
class TypeInfo<T &> {<!-- -->
public:
    enum {<!-- --> IsPtrT = 0, IsRefT = 1, IsArrayT = 0 }; // Define three enumeration constants, indicating that T & amp; is not a pointer, a reference, or an array
    typedef T baseT; // define typedef as T type
    typedef typename TypeInfo<T>::bottomT bottomT; // define typedef as TypeInfo<T>::bottomT type
};

// Type template specialization to provide type information for an array type T[N] of size N
template<typename T, size_t N>
class TypeInfo <T[N]> {<!-- -->
public:
    enum {<!-- --> IsPtrT = 0, IsRefT = 0, IsArrayT = 1 }; // Define three enumeration constants, indicating that T[N] is not a pointer, not a reference, but an array
    typedef T baseT; // define typedef as T type
    typedef typename TypeInfo<T>::bottomT bottomT; // define typedef as TypeInfo<T>::bottomT type
};

Reduce code bloat

Definition: Whenever we use a type as a template parameter to instantiate a class template or function template, the C++ compiler will generate a class or function corresponding to the type, which will cause repeated generation for each instantiation , resulting in code bloat

example

// Define a Vector type object VPVector, the element type is void*, that is, a pointer to any type
Vector<void*> VPVector;

// Define a template class Vector<T*>, where T is a pointer type and inherits from VPVector
template<class T>
class Vector <T*>: public VPVector{<!-- -->
public:
    // Overload the [] operator and return a pointer of type T*
    T* & amp; operator[](int i) {<!-- -->
        return (T* & amp;)(VPVector::operator[](i));
    };
};

int main()
{<!-- -->
    // Define an object v1 of type Vector<int*> to store pointers of type int
    Vector<int*> v1;

    // Define an object v2 of type Vector<double*> to store pointers of type double
    Vector<double*> v2;

    // Define a variable i of type int and initialize it to 3, assign the address of i to the first element in v1
    int i = 3;
    v1[0] = &i;

    // Define a variable d of double type and initialize it to 3.14, assign the address of d to the first element in v2
    double d = 3.14;
    v2[0] = &d;
}