C++ Templates

Chapter III Category template

Estimated reading time of 8 minutes 19 Views

Similar to a function, a class can be defined by one or more types of parameters. For example, packagings are usually used to manage a particular element. The container category can be achieved by using a class template without the need to determine the type of element in the container.

3.1 类模板 Stack 的实现

A simple type template Stack achieves the following:

#include <iostream>
#include <stdexcept>

template<typename T>
class Stack{
public:
    void push(T const&);
    void pop();
    T top() const;
    bool empty() const{ elems.empty(); }
private: std::vector<T> elems;
};

template<typename T>
void Stack<T>::push(T const& elem){
    elems.emplace_back(elem);
}

template<typename T>
void Stack<T>::pop(){
    if(elems.empty()) throw std::out_of_range("Stack<>::pop: empty stack");
    elems.pop_back();
}

template<typename T>
T Stack<T>::top() const{
    if(elems.empty()) throw std::out_of_range("Stack<>::top(): empty stack");
    return elems.back();
}
C++

The above class template Stack is achieved by a class template vector in the C++ Standard Library, with the advantage that memory management, copy construction functions and grant operators do not need to be performed in person.

3.1.1 类模板的声明

The statements of the type template are essentially similar to those of the function template, i.e., the identification of the type parameter is declared before the declaration:

  • UsetypenameKeyword:
template<typename T>
class Stack{ };
C++
  • UseclassKeyword:
template<class T>
class Stack{ };
C++

Inside the type template, T As in any other type, it can be used to declare a member variable and a member function, in the following examples:

  • TUsed in statementsstd::vectorType of element
  • Statementpush()It's an acceptance.const T&As the only active member function
  • Statementtop()is the return typeTMember Functions
template<typename T>
class Stack{
public:
    Stack();
    void push(T const&);
    void pop();
    T top() const;
private: std::vector<T> elems;
};
C++

The type is... Stack<T> where T is the template parameter. Therefore, when the type of use of the category is required in the declaration, it must be used Stack<T>, for example, to declare a copy tectonic function and an attribute operator performed by yourself:

template<typename T>
class Stack{
    /* copy constructor */
    Stack(const Stack<T>&);
    /* copy asssignment */
    Stack<T>& operator=(const Stack<T>&);
}
C++

However, when a class name is used instead of a class type, it should be used only. Stack , e.g. the name of the specified class, the tectonic function of the class, and the resolution function.

3.1.2 成员函数的实现

To define a member function of a class template, you need to specify a function template for that member and use the full type qualifier for this category.

  • TypeStack<T>Member Functionspush()This has resulted in:
template<typename T>
void Stack<T>::push(const T& elem){
    elems.emplace_back(elem);
}
C++
  • TypeStack<T>Member Functionspop()This has resulted in:
template<typename T>
T Stack<T>::pop(){
    if(elems.empty()) throw std::out_of_range("Stack<>::pop: empty Stack");
    /* leave out Exception Security */
    T elem = elems.back();
    elems.pop_back();
    return elem;
}
C++
  • TypeStack<T>Member Functionstop()This has resulted in:
template<typename T>
T Stack<T>::top const{
    if(elems.empty()) throw std::out_of_range("Stack<>::top(): empty Stack");
    return elems.back();
}
C++

A member function of the class can, of course, be directly achieved as an inline function in the class by:

template<typename T>
class Stack{
    ..
    void push(const T& elem) { elems.push_back(elem); }
    ..
};
C++

3.2 类模板的 Stack 的使用

#include <iostream>
#include <stdexcept>

template<typename T>
class Stack{
public:
    void push(T const&);
    void pop();
    T top() const;
    bool empty() const{ elems.empty(); }
private: std::vector<T> elems;
};

template<typename T>
void Stack<T>::push(T const& elem){
    elems.emplace_back(elem);
}

template<typename T>
void Stack<T>::pop(){
    if(elems.empty()) throw std::out_of_range("Stack<>::pop: empty stack");
    elems.pop_back();
}

template<typename T>
T Stack<T>::top() const{
    if(elems.empty()) throw std::out_of_range("Stack<>::top(): empty stack");
    return elems.back();
}

int main(){
    try{
        Stack<int> intStack;
        Stack<std::string> stringStack;

        intStack.push(7);
        std::cout << intStack.top() << '\n';

        stringStack.push("hello");
        std::cout << stringStack.top() << '\n';
        stringStack.pop();
        stringStack.pop(); /* error */

    }catch(cosnt std::exception& ex){
        std::cerr << "Exception: " << ex.what() << '\n';
        return EXIT_FAILURE;
    }
}
C++

Type of declaration adopted Stack<int> , you can use it inside the template. int Example T And so, intStack It was created from Stack<int> object, the element of which is stored in 'std::vector' and is typed as int, and all called member functions are examples based on int function of type, same Stack<std::string> So is it.

  • Notes:
    • Only those member functions that are called will give rise to exemplification codes for those functions.
    • For class templates, a member function is exemplified only when used.
    • The benefits of doing so are:
      • Save space and time.
      • For those types that " fail to provide all operations in all member functions " , it is also possible to use this type of exemplify type templates as long as they are not used inside the template.
      • Supplement: What is the "failure to provide certain operations" member function:
          1. operator<
          2. operator==
          3. operator()
          4. ......
        • If none of these operations are implemented in the class template, they cannot be used (the default for these operations cannot be applied in the current template class).

In the above example, default construction, push()andtop()It's all an example. int Examples and std::string version, and pop() Just one example. std::string Version.
In addition, if the type template contains some static members, each of the types used to exemplify them will be exemplified.

An exemplified type of template can be used as any other type, as long as it supports the action to be called:

void foo(const Stack<int>& s){
  Stack<int> istack[10];
  ...
}

typedef Stack<int> IntStack;
void foo(const IntStack& s){
  IntStack istack[10];
}
C++

Use typedef Just for... Stack<int> A "type alias" has been taken and no new type has been defined.
Thus, after defining a new type:

typedef Stack<int> IntStack;
C++

IntStack and Stack<int> It remains the same type and can be used to value each other. The template may be of any type:

  • Floating pointer:Stack<float*>
  • intType of bar:Stack<Stack<int>>

The only requirement is that the type must provide all the operations called, i.e. all the operators as follows:==<and>All need to be reloaded.

3.3 类模板的特化

Similar to the reloading of the function template, a specialized type template can be used with the actual involvement of the template, and a specialized type template can be used to optimize achievement based on a particular type, or to solve a problem that arises when a specific type is used as an example.
In addition, if a type template is to be specialized, all member functions of that type of template will need to be specialized, and although only one member function can be specialized, this practice does not characterize the entire category or the entire category.

To characterize a type template, you need to declare one at the start template<> , and then declare the type of template that you want to use. This type is used as the template and must be specified directly after the class name:

template<>
class Stack<std::string>{
  ...
};
...
C++

When specializing a class template, each member function must be redefined as a normal function, and each T of the original template functions is replaced by the type of specialisation accordingly:

template<typename T>
class Stack{
public:
    /* declare functions */
    void push(T const&);
    void pop();
    T top() const;
    bool empty() const{ return elems.empty(); }

private:
    std::vector<T> elems;
};

    /* implement functions */
template<typename T>
void Stack<T>::push(T const& elem){
    elems.push_back(elem);
}

template<typename T>
void Stack<T>::pop(){
    if(elems.empty()) throw std::out_of_range("Stack<>::pop(): empty stack");
    elems.pop_back();
}

template<typename T>
T Stack<T>::top() const{
    if(elems.empty()) throw std::out_of_range("Stack<>::top(): empty Stack");
    return elems.back();
}

/* a specified template for std::stirng */
template<>
class Stack<std::string>{
public:
  void push(const std::string&);
  void pop();
  std::string top() const;
  bool empty() const { return elems.empty(); }
private: deque<std::string> elems;
}

void Stack<std::string>::push(const std::string elem){
  elems.emplace_back(elem);
}

void Stack<std::string>::pop(){
  if(elems.empty()) std::out_of_range("Stack<std::string>::pop(): empty Stack");
  elems.pop_back();
}

std::string Stack<std::string>::top const{
  if(elems.empty()) std::out_of_range("Stack<std::string>::top(): empty Stack");
  return elems.back();
}
C++

The above example shows that the achievement of specialization can be completely different from that of the basic type (primary test).

3.4 局部特化

Class templates can be localized, they can be specified for specific achievement in a given environment and require that certain parameters still need to be defined by the user, such as the class templates:

template<typename T1, typename T2>
class MyClass{
  ...
};
C++

There are several other local features available from above:

  • Localization: two template parameters of the same type
template<typename T>
class MyClass<T, T>{
  ...
};
C++
  • Localization: The second template parameter is of other types, such as:int
template<typename T>
class MyClass<T, int>{
  ...
};
C++
  • Localization: both template parameters refer to needle type
template<typename T>
class MyClass<T1*, T2*>{
  ...
};
C++
  • Examples of specific type templates:
MyClass<int, float> mif;    // use MyClass<T1,T2>, two different types
MyClass<float, float> mff;  // use MyClass<T,T>, two same types
MyClass<float, int> mfi;    // use MyClass<T, int>, specified the second param with int
MyClass<int*, float*> mp;   // use MyClass<T1*, T2*>, two different types pointer
C++

If multiple localizations match a statement to the same extent, the declaration is described as having a dual effect:

MyClass<int, int> m;    // error: as same as this matches MyClass<T, T> and MyClass<T, int>
MyClass<int*, int*> m;  // error: as same as this matches Myclass<T, T> and MyClass<T1*, T2*>
C++

The second type of duality can be addressed by providing an additional feature pointing to the same type of pointer:

template<typename T>
class MyClass<T*, T*>{
  ...
};
C++

3.5 缺省模板参数

For class templates, default values can also be defined for template parameters, which are referred to as default templates, and they can also refer to previous template parameters.
For example, in category Stack<> , can be defined as the 2nd template parameter for the management element and use std::vector<> As its default value:

#include <vector>
#include <stdexcept>

template<typename T, typename CONT = std::vector<T>>
class Stack{
public:
  void push(const T&);
  void pop();
  T top() const;
  bool empty() const { return elems.empty(); }
private:
  // equivalent to
  // typedef
  // std::vector<int /* or other specific type */>
  // CONT
  CONT elems;
};

template<typename T, typename CONT>
void Stack<T, CONT>::push(const T& elem){
  elems.emplace_back(elem);
}

template<typename T, typename CONT>
void Stack<T, CONT>::pop(){
  if(elems.empty()) throw std::out_of_range("Stack<>::pop(): empty Stack");
  elems.pop_back();
}

template<typename T, typename CONT>
T Stack<T, CONT>::top() const{
  if(elems.empty()) throw std::out_of_range("Stack<>::top(): empty Stack");
  return elems.back();
}
C++

In the code above, the class template contains two template parameters, so each member function must be defined with both parameters:

template<typename T, typename CONT>
void Stack<T, CONT>::push(const T& elem){
  elems.emplace_back(elem);
}
C++

If only the first type is actually referred to this type of template, it will be usedvectorTo manage it.stackElements:

template<typename T,
         typename CONT =
              std::vector<T>>
class Stack{
  ...
private:
  CONT elems;
  ...
};
C++

When declaring the Stack object in the program, you can also specify the type of container:

#include <iostream>
#include <stdexcept>
#include <deque>
#include <cstdlib>
#include <vector>

template<typename T,
         typename CONT = std::vector<T>>
class Stack{
public:
  void push(const T&);
  void pop();
  T top() const;
  bool empty() { return elems.empty(); }
private: CONT elems;
};

template<typename T, typename CONT>
void Stack<T, CONT>::push(const T& elem){
  elems.emplace_back(elem);
}

template<typename T, typename CONT>
void Stack<T, CONT>::pop(){
  if(elems.empty()) throw std::out_of_range("Stack<>::pop(): empty Stack");
  elems.pop_back();
}

template<typename T, typename CONT>
T Stack<T, CONT>::top() const{
  if(elems.empty()) throw std::out_of_range("Stack<>::top(): empty Stack");
  return elems.back();
}

int main(){
  try{
    Stack<int> intStack;
    Stack<double, std::deque<dobule>> dblStack;

    intStack.push(7);
    std::cout << intStack.top() << '\n';
    intStack.pop();

    dblStack.push(42.42);
    std::cout << dblStack.top() << '\n';
    dblStack.pop();
    dblStack.pop();
  }catch(const std::exception& ex){
    std::cerr << "Exception: " << ex.what() << '\n';
    return EXIT_FAILURE;
  }
}
C++
  • UseStack<double, std::deque<double>>You can declare an "Element type is double " and usestd::deque<>Manages the in-house elements' stack.

3.6 小结

  • Class template is a category in which one or more of the types can be achieved without being specified.
  • For the use of a class template, a specific type can be entered as a template, and the compiler will then use that type as the basis for case-based templates.
  • For class templates, only those member functions that are called will be exemplified.
  • A specific type of specialisation type template can be used.
  • A specific type of local specialisation type template can be used.
  • You can define default values for the parameters of a class template, which can also refer to previous template parameters.

Leave a Comment

Share this Doc

Chapter III Category template

Or copy link

CONTENTS
Remember to rest.