C++ 模板与泛型编程
模板是 C++ 实现泛型编程的基础,它允许我们编写与类型无关的代码。本章将详细介绍函数模板、类模板、特化和模板元编程。
函数模板
基本概念
函数模板定义了一族函数,编译器会根据调用时的参数类型生成具体的函数:
// 模板函数:比较两个数的大小
template<typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
int main() {
// 编译器自动生成 max<int>, max<double> 等版本
std::cout << max(3, 5) << std::endl; // 5
std::cout << max(3.14, 2.71) << std::endl; // 3.14
std::cout << max('a', 'z') << std::endl; // 'z'
return 0;
}
语法解释
template<typename T>
// template<class T> // class 和 typename 等价
//
// typename/class 后面的名字是模板参数(可以任意命名)
// 习惯上使用 T, U, V 等名字
T max(T a, T b) {
return (a > b) ? a : b;
}
多个模板参数
// 多个模板参数
template<typename T1, typename T2>
void printPair(T1 a, T2 b) {
std::cout << a << " - " << b << std::endl;
}
// 返回类型也可以是模板参数
template<typename T1, typename T2>
T1 max2(T1 a, T2 b) {
return (a > b) ? a : b; // 返回类型是 T1
}
自动类型推导
编译器会自动推导模板参数:
template<typename T>
void swap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
int main() {
int x = 1, y = 2;
swap(x, y); // T 被推导为 int
std::string s1 = "hello", s2 = "world";
swap(s1, s2); // T 被推导为 std::string
return 0;
}
显式指定模板参数
当编译器无法推导或需要指定特定类型时:
template<typename T>
T add(T a, T b) {
return a + b;
}
int main() {
// 显式指定模板参数
std::cout << add<double>(3, 4.14) << std::endl; // 7.14
// 某些情况必须显式指定
int (*func)(int, int) = add<int>; // 获取函数指针
return 0;
}
类模板
基本类模板
template<typename T>
class Box {
private:
T value;
public:
Box(T v) : value(v) {}
T getValue() const { return value; }
void setValue(T v) { value = v; }
};
int main() {
Box<int> intBox(42); // 整数盒子
Box<std::string> strBox("hello"); // 字符串盒子
std::cout << intBox.getValue() << std::endl; // 42
std::cout << strBox.getValue() << std::endl; // hello
return 0;
}
类模板成员函数
template<typename T>
class Stack {
private:
std::vector<T> data;
public:
void push(const T& item) {
data.push_back(item);
}
T pop() {
if (data.empty()) {
throw std::runtime_error("Stack is empty");
}
T item = data.back();
data.pop_back();
return item;
}
bool empty() const {
return data.empty();
}
size_t size() const {
return data.size();
}
};
默认模板参数
template<typename T = int>
class Container {
private:
T value;
public:
Container(T v = T()) : value(v) {}
T get() const { return value; }
};
int main() {
Container<> c1; // 使用默认类型 int
Container<double> c2(3.14); // 使用 double
Container<std::string> c3("test"); // 使用 string
return 0;
}
模板类特化
全特化
为特定类型提供特殊实现:
template<typename T>
class Compare {
public:
static bool equal(T a, T b) {
return a == b;
}
};
// 特化版本:针对 char* 的特殊比较
template<>
class Compare<char*> {
public:
static bool equal(const char* a, const char* b) {
return strcmp(a, b) == 0;
}
};
int main() {
std::cout << Compare<int>::equal(1, 1) << std::endl; // 1
std::cout << Compare<char*>::equal("hello", "hello") << std::endl; // 1
return 0;
}
偏特化
部分指定模板参数:
template<typename T, typename U>
class Pair {
public:
T first;
U second;
};
// 偏特化:两个类型相同时
template<typename T>
class Pair<T, T> {
public:
T first;
T second;
// 额外的成员函数
T sum() const { return first + second; }
};
可变参数模板(C++11)
模板参数包
// 可变参数函数模板
template<typename... Args>
void print(Args... args) {
// 使用展开操作
((std::cout << args << " "), ...);
std::cout << std::endl;
}
int main() {
print(1, 2, 3); // 输出: 1 2 3
print("hello", 42, 3.14); // 输出: hello 42 3.14
print(); // 输出: 空行
return 0;
}
递归终止
// 递归终止函数
void print() {
std::cout << std::endl;
}
// 递归处理参数
template<typename T, typename... Args>
void print(T first, Args... rest) {
std::cout << first << " ";
print(rest...);
}
int main() {
print(1, 2, 3, 4, 5);
return 0;
}
可变参数类模板
// 可变参数元组
template<typename... Types>
class Tuple;
template<typename T, typename... Types>
class Tuple<T, Types...> {
private:
T first;
Tuple<Types...> rest;
public:
Tuple(T f, Types... r) : first(f), rest(r...) {}
T getFirst() const { return first; }
auto getRest() const -> Tuple<Types...> { return rest; }
};
// 特化:空元组
template<>
class Tuple<> {};
模板高级特性
模板作为模板参数
template<typename T, template<typename> class Container>
class Wrapper {
private:
Container<T> data;
public:
void add(const T& item) {
data.push_back(item);
}
};
int main() {
Wrapper<int, std::vector> w;
w.add(1);
w.add(2);
return 0;
}
类型别名模板(C++11)
// 使用 using 定义模板别名
template<typename T>
using Vec = std::vector<T>;
template<typename K, typename V>
using Map = std::map<K, V>;
int main() {
Vec<int> v = {1, 2, 3};
Map<std::string, int> m;
m["key"] = 42;
return 0;
}
模板约束(C++20 concepts)
C++20 引入的概念(Concepts)可以约束模板参数:
#include <concepts>
// 定义概念
template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;
// 使用概念约束模板参数
template<Numeric T>
T add(T a, T b) {
return a + b;
}
int main() {
std::cout << add(1, 2) << std::endl; // 3
std::cout << add(1.5, 2.5) << std::endl; // 4.0
// add("hello", "world"); // 编译错误!不满足 Numeric
return 0;
}
if constexpr(C++17)
编译时条件分支:
template<typename T>
void printValue(const T& value) {
if constexpr (std::is_integral_v<T>) {
std::cout << "Integer: " << value << std::endl;
} else if constexpr (std::is_floating_point_v<T>) {
std::cout << "Floating: " << value << std::endl;
} else {
std::cout << "Other: " << value << std::endl;
}
}
int main() {
printValue(42); // Integer: 42
printValue(3.14); // Floating: 3.14
printValue("hello"); // Other: hello
return 0;
}
标准库中的模板
std::pair
#include <utility>
int main() {
std::pair<int, std::string> p(1, "one");
// 访问元素
std::cout << p.first << std::endl; // 1
std::cout << p.second << std::endl; // one
// 使用 make_pair
auto p2 = std::make_pair(2, "two");
// 解构(C++17)
auto [num, str] = p;
return 0;
}
std::tuple
#include <tuple>
int main() {
// 创建元组
auto t = std::make_tuple(1, "hello", 3.14);
// 访问元素
std::cout << std::get<0>(t) << std::endl; // 1
std::cout << std::get<1>(t) << std::endl; // hello
// 解构(C++17)
auto [a, b, c] = t;
// 元组大小
constexpr size_t size = std::tuple_size_v<decltype(t)>;
return 0;
}
std::function 与 std::bind
#include <functional>
int add(int a, int b) { return a + b; }
int main() {
// 函数指针
std::function<int(int, int)> func = add;
std::cout << func(3, 5) << std::endl;
// lambda
func = [](int a, int b) { return a * b; };
std::cout << func(3, 5) << std::endl;
// bind 绑定参数
auto add5 = std::bind(add, 5, std::placeholders::_1);
std::cout << add5(10) << std::endl; // 15
return 0;
}
练习
- 实现一个通用的 find 函数,查找数组中的元素
- 实现一个模板类 Matrix,支持矩阵运算
- 使用可变参数模板实现一个日志打印函数
- 实现一个类型转换函数模板
小结
本章学习了:
- 函数模板:泛型函数定义和使用
- 类模板:泛型类定义和使用
- 模板特化:全特化与偏特化
- 可变参数模板:处理任意数量的参数
- 模板高级特性:类型别名、概念、if constexpr
下一步
接下来让我们学习 C++ STL 标准库,了解常用容器和算法。