C++ 模板编程技术解析

非法关键字 / 2023-07-20 / 原文

一、函数模板

函数模板实现通用函数,根据传递类型进行编译时实参推导:

template <typename T> 
T add(T a, T b) {
  return a + b;
}

int main() {
  int x = 1, y = 2;
  double m = 1.5, n = 2.5;
  
  int z = add(x, y); 
  double p = add(m, n);

  return 0;  
}

这里template <typename T>声明了一个泛型类型T,然后使用T作为add函数的参数和返回值类型。这使得add可以接受不同类型的a,b,并返回相应类型的和。编译器会根据传参类型实时实例化add。

二、类模板

类模板生成可以根据类型参数化的类:

template <typename T>
class Stack {
private:
  T arr[100];
  int top;
public:
  void push(T);
  T pop(); 
};

int main() {
  Stack<int> s1;
  Stack<double> s2;
  
  s1.push(1);
  s2.push(1.5);

  return 0;
}

类模板的语法类似,template后声明类型参数,然后在类中使用该类型参数,来定义可以通用的类。Stack类就可以因类型T的不同而实例化出Stack, Stack等等。

三、模板特化

模板特化用于自定义某个类型的实现:

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

// 特化int的max实现
template <>
int max<int>(int a, int b) {
  cout << "Int specialization";
  return a > b ? a : b; 
}

int main() {
  int x = 1, y = 2;
  double m = 1.5, n = 2.5;

  max(x, y); //calls int specialization
  max(m, n); //calls original

  return 0;
}

这里使用template<> 的语法,为int类型的max定制了一个专属实现。这让我们不影响原模板的同时,可定制某些特定类型。

四、偏特化

偏特化用于定制模板的部分参数类型:

// 原类模板
template<typename T1, typename T2> 
class Test {
  public:
    T1 value1;
    T2 value2;
    
    Test(T1 v1, T2 v2) {
      value1 = v1;
      value2 = v2;
    }
};

// 偏特化版本,针对T2为int的情况
template<typename T>
class Test<T, int> {
  public:
    T value1;
    int value2;
    
    Test(T v1, int v2) {
      value1 = v1;
      value2 = v2; 
    }
};

int main() {
  
  // 使用原类模板 
  Test<int, double> t1(1, 2.5); 

  // 使用偏特化版本
  Test<double, int> t2(2.3, 5);

  return 0;
}

这样通过一个具体的类定义,就可以更清晰地展示偏特化的用法:

  • 原模板接受两个类型T1和T2
  • 偏特化版本 fixing T2为int类型
  • 根据实参类型不同,分别实例化原类模板和偏特化版本

五、递归模板

模板可以递归调用自己,实现编译期计算:

cppCopy codetemplate<int N>
struct Fib {
  static const int val = Fib<N-1>::val + Fib<N-2>::val;
};

template<>
struct Fib<0> {
  static const int val = 0;  
};

int x = Fib<5>::val; // 编译期计算斐波那契数列

每次实例化时(如Fib<5>),递归调用会展开模板自身,直到终止条件。计算最终在编译期完成。