跳转至

5 Templates

文本统计:约 771 个字 • 201 行代码

Same function name with different argument-lists.

void print(char * str, int width); // #1
void print(double d, int width); // #2
void print(long l, int width); // #3
void print(int i, int width); // #4
void print(char *str); // #5
print("Pancakes", 15);
print("Syrup"); 
print(1999.0, 10); 
print(1999, 12); 
print(1999L, 15);

为了使 print 函数可以处理不同的参数,我们需要为每一个类型写一个函数,这造成了代码的重复,也不方便后期维护。为此我们引入模板的概念。

我们可以定义函数模板或者类模板,接下来将详细阐述


Function templates

Similar operations on different types of data.

swap 函数为例

template < class T >
void swap( T& x, T& y ) {
     T temp = x;
     x = y;
     y = temp;
}
  • The template keyword introduces the template

  • The class T specifies a parameterized type name (class means any built-in type or UDT)

  • Inside the template, use T as a type name

类型参数 T 可以作为传入函数的参数的类型,返回值的类型,函数内局部变量的类型。编译器会根据模板参数生成一份相应的函数,并对其进行语法检查,这个函数只有在实例化的时候才会出现。

int i = 3; int j = 4;
swap(i, j); // use explicit int swap
float k = 4.5; float m = 3.7;
swap(k, m); // instantiate float swap
std::string s("Hello");
std::string t("World");
swap(s, t); // instantiate std::string swap

模板也可以特化,函数模板只能完全特化

emplate <typename T>
void func(T param) {
    // 通用实现
}

// 完全特化:针对int类型的特化版本
template <>
void func<int>(int param) {
    // int类型的特殊实现
}

模板函数进行推导的时候不会对参数进行隐式转换,只会匹配相应的版本

swap(int, int); // ok
swap(double, double); // ok
swap(int, double); // error!

模板函数也可以显示地推导

template <class T>
void foo() { /* ... */ }
foo<int>(); // type T is int
foo<float>(); // type T is float

然后模板函数和普通函数是可以共存的,调用顺序如下

  • First, check for exact regular-function match

  • Then, check for exact function-template match

  • Last, implicit conversions for regular functions

void f(float i, float k) { /*...*/ };
template <class T> void f(T t, T u) { /*...*/ };
f(1.0f, 2.0f); //调用的是第一个函数
f(1.0, 2.0);//调用的是第二个函数
f(1, 2);//这里调用的也是第二个函数
f(1, 2.0);//这里调用的是第一个函数,做了隐式转换

Class templates

类模板使用类型参数来定义类,可以为同一个类结构指定不同的数据类型,最经典的用处为容器的类模板,比如说 stacklistqueue 等等,下面是 Vector 容器的实现

template <class T>
class Vector {
public:
 Vector(int);
 ~Vector();
 Vector(const Vector&);
 Vector& operator=(const Vector&);
 T& operator[](int);
private:
 T* m_elements;
 int m_size;
}

使用方法就如下

Vector<int> v1(100);
Vector<Complex> v2(256);
v1[20] = 10;
v2[20] = Complex(cos(pi/4), sin(pi/4));

相应的成员函数如下

template <class T>
Vector<T>::Vector(int size): m_size(size) {
 m_elements = new T[m_size];
}
template <class T>
T& Vector<T>::operator[](int index) {
 if(index < m_size && index >= 0) {
 return m_elements[index];
 } else {
 /*...*/
 }
}

类模板的特化

template <typename T, typename U>
class MyTemplate {
public:
    void print(T t, U u) {
        std::cout << "Generic: " << t << ", " << u << std::endl;
    }
};

下面是一个完全特化

template <>
class MyTemplate<int, double> {
public:
    void print(int i, double d) {
        std::cout << "Specialized int and double: " << i << ", " << d << std::endl;
    }
};

下面是一个部分特化

template <typename T>
class MyTemplate<T, double> {
public:
    void print(T t, double d) {
        std::cout << "Partially specialized with double: " << t << ", " << d << std::endl;
    }
};

template <typename T>
class MyTemplate<T, T*> {
public:
    void print(T t, T* d) {
        std::cout << "Partially specialized : " << t << ", " << d << std::endl;
    }
};

模板可以拥有多个类型参数,比如说下面这个类模板

template < class Key, class Value >
class HashTable {
 const Value& lookup (const Key&) const;
 void insert (const Key&, const Value&);
 /* ... */
}

模板也可以被嵌套,毕竟一个模板加上类型参数就可以被认为是一个新的类型了

Vector< int (*) (Vector<double>&, int) >

Expression parameters:模板中还可以有非类型参数,比如说

template <class T, int bounds = 100>
class FixedVector {
public:
 FixedVector();
 T& operator[](int);
private:
 T elements[bounds]; // fixed-size array!
}

使用方法如下

FixedVector<int, 50> v1;
FixedVector<int, 10*5> v2;
FixedVector<int> v3; // => FixedVector<int, 100>

Member templates

Template declarations can appear inside a memberspecification of any class

比如说下面复数类中的构造函数

template<typename T> class complex
{
public:
 template<class X> complex(const complex<X>&);
/* ... */
};

使用场景如下

complex<double> c1(3.0, 4.0);
complex<float> c2(5.0f, 6.0f);

// 使用模板构造函数将 c2 转换为 complex<double>
complex<double> c3(c2);  // 此处调用了 template<class X> complex(const complex<X>&) 构造函数

关于模板类的继承,可以继承于一个非模板类,也可以继承于一个模板类。非模板类也可以继承于一个实例化的模板类

template <class A>
class Derived : public Base { /* ... */ }

template <class A>
class Derived : public List<A> { /* ... */ }

class SupervisorGroup : public
 List<Employee*> { /* ... */ }

CRTP (The Curiously Recurring Template Pattern)

一种特殊的模板使用方式,也是一种实现多态的方式。与传统的通过虚函数实现的动态多态不同,CRTP可以在编译期解决派生类的具体类型,从而提高运行时效率。

基本的框架如下,基类为模板类,派生类将自身作为模板参数给到基类

template <class T>
class Base
{
 /* ... */
};
class Derived : public Base<Derived>
{
 /* ... */
};

下面是一个利用 CRTP 实现多态的例子

#include <iostream>

// 定义基类模板
template <typename Derived>
class Base {
public:
    void interface() {
        // 调用实现于派生类中的方法
        static_cast<Derived*>(this)->implementation();
    }

    static void static_interface() {
        Derived t;
        t.implementation();
    }
};

// 定义第一个派生类
class Derived1 : public Base<Derived1> {
    friend class Base<Derived1>; // 确保Base可以访问Derived1的私有成员
private:
    void implementation() {
        std::cout << "Derived1 implementation" << std::endl;
    }
};

// 定义第二个派生类
class Derived2 : public Base<Derived2> {
    friend class Base<Derived2>; // 确保Base可以访问Derived2的私有成员
private:
    void implementation() {
        std::cout << "Derived2 implementation" << std::endl;
    }
};

int main() {
    Derived1 d1;
    Derived2 d2;

    // 通过基类接口调用派生类的方法
    Base<Derived1>& b1 = d1;
    b1.interface(); // 输出: Derived1 implementation

    Base<Derived2>& b2 = d2;
    b2.interface(); // 输出: Derived2 implementation

    // 使用静态接口
    Base<Derived1>::static_interface(); // 输出: Derived1 implementation
    Base<Derived2>::static_interface(); // 输出: Derived2 implementation

    return 0;
}

Morality(一些规范):Put the definition/declaration for templates in the header file

  • won 't allocate storage for the function/class at that point 不会立刻为这个函数或者类分配空间,在实例化的时候分配

  • compiler/linker have mechanisms for removing multiple definitions 编译器或者连接器有一些机制可以移除重复定义

评论区

对你有帮助的话请给我个赞和 star => GitHub stars
欢迎跟我探讨!!!