banner
Matrix

Matrix

Abyss
email
github

Petty C++ Templates

Templates are a powerful feature of C++ generics that can be applied to classes, functions, and variables. The existence of templates allows coders to write type-independent code that generates general classes, functions, and variables at compile time.

Basic Form of Templates#

Templates define a blueprint for generating classes and functions that can operate on any type of data:

Basic Form of Templates#

Taking a common function template as an example, a template is defined as follows:

template <typename T>

T func(T param){
  // do something...
  return param;
}

The above code is a function template with a single type parameter T, and both the parameter and return value are of type T.

  • The keyword template indicates the definition of a template.
  • The keyword typename is a placeholder for the parameter type, with T as the template parameter. At compile time, the compiler replaces the type instances of T with the specific parameter type based on the function call. This process is called "template instantiation".

When calling, it is called in the same way as a normal function:

int i = func(42);

Class Templates#

template <typename T>
class Array {
 private:
   T* data;
   size_t size;
 public:
   explicit Array(size_t size): size(size), data(new T[size]) {}
   ~Array() {delete[] data;}
  
   T& operator[](size_t index) {return data[index];}
}

Function Templates#

template <typename T>
T max(T a, T b) {
  return (a > b)? a:b;
}

Variable Templates#

Variable templates are a new feature introduced in C++14 and can generate static variables of any type:

template <typename T>
constexpr T pi = T(3.1415)

Template Type Parameters#

Templates are not limited to a specific number of parameters, similar to regular functions:

template <typename T, typename U, typename V>

Any number of type parameters can be defined for a template:

template<typename... Args> class vtclass;

template<typename... Args> 
void func(Arguments... args)

In C++17 and later, automatic deduction of types can be used. When auto is used in the template parameter position, it allows the template to accept any type of non-type template parameter, including but not limited to integers, characters, and booleans. Such templates are called auto-typed non-type template parameters:

template <auto x> constexpr auto constant = x;

The template parameter <auto x> means that constant can accept any type of constant expression as a parameter. The value of constant will be the value of the parameter x passed to it.

Default Template Variables#

The declaration of default template variables is the same as regular functions:

template <typename T = int>
void func(T factor){
  // do something
}

func<>(42);

Template Specialization#

Templates provide powerful generic capabilities, but when defining a template, we do not want to write repetitive code for every case. In many cases, we only want different execution paths when the parameter types are different. This is where template specialization is needed.

template <typename T, typename U>

class MyClass{}; // Define the template

template <typename T>
class MyClass<T, int>{} // Template specialization

MyClass<int, string> myClass1; // Original template

MyClass<int, int> myClass2; // Template specialization

Class Templates#

Member Functions of Class Templates#

Functions can be defined in class templates or externally. When template member functions are defined externally, the template declaration part of the class template needs to be specified.

template<class T, int i>
class MyStack {
    // Class members and method declarations
};

template<class T, int i>
void MyStack<T, i>::push(const T item) {
    // do something
}

template<class T, int i>
T& MyStack<T, i>::pop() {
    // do something
}

A more common practice is to directly define template member functions in class templates. This writing style helps the compiler to perform inline processing and improve efficiency. It also simplifies the code.

For general non-template functions or classes, the usual practice is to declare them in the header file and implement them in the source file. However, in template programming, in order to ensure that the template can access the complete template definition at compile time, it is best practice to directly place the template definition and declaration in the header file. Otherwise, unless each type of template is manually instantiated, there may be linking errors due to the inability to find the template instance definition during linking.

For complex and large template libraries, it is often the best practice to place the implementation code of the template in .ipp or .tpp files, and then include them at the bottom of the header file:

// MyTemplate.h
template<typename T>
class MyTemplate {
  public:
    void func(T value);
};

#include "MyTemplate.tpp"
// MyTemplate.tpp, actual implementation of the template
template<typename T>
void MyTemplate<T>::doSomething(T value) {
    // Method implementation
}

Nested Class Templates#

Templates can be nested:

#include <iostream>

template<class T>
class A{
public:
    template <class U> 
    class B{
    private:
        U u; // Use a value of type U instead of a pointer to simplify the example
    public:
        B() {} // Constructor implementation
        U& Value() { return u; } // Return a reference to a value of type U
        void print() { std::cout << u << std::endl; } // Print the value of type U
        ~B() {} // Destructor implementation
    };

    B<int> b;

public:
    A(T t) { b.Value() = t; }
    void print() { b.print(); } // Call print of B<int>
};

// Syntax example when defining nested class template member functions externally
template<class T>
template<class U>
A<T>::B<U>::B() {
    // Constructor implementation
}

template<class T>
template<class U>
U& A<T>::B<U>::Value() {
    // This function can return a reference of type U, this is just an example
    return u;
}

int main() {
    A<int> a(42); // Create an instance of class A
    a.print(); // Print 42
}

Function Templates#

Function Template Instantiation#

Functions can be instantiated using explicit specification or deduction:

template<class T> void f(T) { }

template void f<int> (int);

template void f(char);

The extern keyword can be used to avoid duplicate code generation (instantiation) and achieve fine control over the code.


template<typename T, std::size_t N>
class MyClass {};

exterm template MyClass<int, 4>::MyClass(void);
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.