基本概念
C++中的模板编程是一种强大且灵活的编程技术,允许编写通用代码。模板通过参数化数据类型或操作,使得程序可以处理不同类型的数据,而无需重复编写类似代码。这种技术广泛应用于泛型编程,例如C++标准模板库(STL)中的容器、迭代器和算法就是基于模板实现的。
函数模板允许编写适用于不同类型的通用函数。你可以用一个模板参数定义一个函数,而不必为每种类型重写函数。
函数模板
#include <iostream>
using namespace std;
template <typename T>
T add(T a, T b) {
return a + b;
}
int main() {
cout << "Add integers: " << add(5, 3) << endl; // 调用时推断 T 为 int
cout << "Add doubles: " << add(2.5, 3.1) << endl; // 调用时推断 T 为 double
return 0;
}
template <typename T>
:这里T是一个类型模板参数,表示这个函数适用于任何类型T。- 函数add可以接收任意相同类型的两个参数,并返回它们的和。编译器会根据调用时的参数类型推导出T。
类模板
#include <iostream>
using namespace std;
template <typename T>
class Box {
T value;
public:
Box(T val) : value(val) {}
T getValue() { return value; }
};
int main() {
Box<int> intBox(123); // Box 类的 int 实例
Box<string> strBox("Hello"); // Box 类的 string 实例
cout << "Int Box: " << intBox.getValue() << endl;
cout << "String Box: " << strBox.getValue() << endl;
return 0;
}
模板特化
模板的一个强大功能是模板特化。模板特化允许针对某个特定类型进行特殊化处理,即可以为某个类型提供一个特殊的实现。
全特化
全特化是指对模板的特定类型进行完全特化处理。
#include <iostream>
using namespace std;
template <typename T>
class Box {
public:
T value;
Box(T val) : value(val) {}
void print() { cout << "Value: " << value << endl; }
};
// 对 char* 类型进行特化
template <>
class Box<char*> {
public:
char* value;
Box(char* val) : value(val) {}
void print() { cout << "String: " << value << endl; }
};
int main() {
Box<int> intBox(123);
Box<char*> strBox("Hello Template");
intBox.print();
strBox.print(); // 调用特化版本
return 0;
}
Box<char*>
是Box类的特化版本,针对char*
类型的模板进行了不同的实现。这样可以在处理字符串时,提供与一般模板不同的行为。
偏特化
偏特化允许我们对某些模板参数进行特化,而不需要对所有模板参数进行特化。
#include <iostream>
using namespace std;
// 泛型类模板
template <typename T1, typename T2>
class Pair {
public:
T1 first;
T2 second;
Pair(T1 f, T2 s) : first(f), second(s) {}
void print() { cout << first << ", " << second << endl; }
};
// 偏特化: 当两个类型相同时,采用特化版本
template <typename T>
class Pair<T, T> {
public:
T first;
T second;
Pair(T f, T s) : first(f), second(s) {}
void print() { cout << "Same type pair: " << first << ", " << second << endl; }
};
int main() {
Pair<int, double> p1(1, 2.5);
Pair<int, int> p2(3, 4); // 调用偏特化版本
p1.print();
p2.print(); // 调用偏特化版本
return 0;
}
模板元编程
模板不仅仅用于定义数据结构和函数,还可以进行编译期的计算,称为模板元编程。模板元编程允许通过递归模板在编译期执行逻辑运算,减少运行时的开销。
编译期阶乘计算
#include <iostream>
using namespace std;
// 模板递归计算阶乘
template <int N>
struct Factorial {
enum { value = N * Factorial<N - 1>::value };
};
// 基例:Factorial<0> 为 1
template <>
struct Factorial<0> {
enum { value = 1 };
};
int main() {
cout << "Factorial<5>::value = " << Factorial<5>::value << endl;
return 0;
}
Factorial<N>
通过模板递归计算阶乘,直到基例Factorial<0>
终止递归。这一计算是在编译时完成的,因此在运行时没有任何额外的开销。
Factorial<5>::value
会展开成5 * 4 * 3 * 2 * 1
,并在编译时得出结果。
变参模板
变参模板允许传递任意数量的模板参数,使得模板更加灵活。
#include <iostream>
using namespace std;
// 变参模板,处理多个参数
template <typename T, typename... Args>
void print(T first, Args... args) {
cout << first << " ";
if constexpr(sizeof...(args) > 0) {
print(args...); // 递归调用
}
}
int main() {
print(1, 2.5, "Hello", 'c'); // 传递多个不同类型的参数
return 0;
}
template <typename T, typename... Args>
中的Args...
表示可以接受多个模板参数。
print函数递归处理每个参数,直到参数耗尽为止。
SFINAE(Substitution Failure Is Not An Error)
SFINAE是C++模板元编程中非常重要的概念。当模板参数替换失败时,并不导致编译错误,而是选择其他可用的重载版本。这通常用于特性检测和选择性模板实例化。
示例
#include <iostream>
#include <type_traits>
using namespace std;
// 通用模板
template <typename T>
typename enable_if<is_integral<T>::value, T>::type
foo(T t) {
cout << "Integer type: " << t << endl;
return t;
}
// 重载版本,用于非整数类型
template <typename T>
typename enable_if<!is_integral<T>::value, T>::type
foo(T t) {
cout << "Non-integer type: " << t << endl;
return t;
}
int main() {
foo(10); // 调用整数版本
foo(3.14); // 调用非整数版本
return 0;
}
enable_if
结合is_integral
用于条件编译。根据传递的类型是否为整数类型,编译器会选择不同的模板实例化版本。