模板与泛型编程是 C++ 最强大和现代的特性之一,它让你能够编写类型安全且高效的通用代码。
1. 函数模板
基本函数模板
// 模板声明
template<typename T>
int compare(const T& v1, const T& v2) {
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}
// 使用类型推导
void function_template_demo() {
std::cout << compare(1, 2) << std::endl; // T = int
std::cout << compare(3.14, 2.71) << std::endl; // T = double
std::cout << compare("hello", "world") << std::endl; // T = const char*
// 显式指定模板参数
std::cout << compare<double>(1, 3.14) << std::endl; // T = double
}
非类型模板参数
// 非类型模板参数
template<typename T, std::size_t N>
void print_array(const T (&arr)[N]) {
for (std::size_t i = 0; i < N; ++i) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}
template<int Value>
class FixedValue {
public:
int get() const { return Value; }
};
void nontype_template_demo() {
int arr[] = {1, 2, 3, 4, 5};
print_array(arr); // T = int, N = 5
FixedValue<42> fv;
std::cout << fv.get() << std::endl; // 输出 42
}
2. 类模板
基本的类模板
// 类模板声明
template<typename T>
class Stack {
private:
std::vector<T> elements;
public:
// 模板类的成员函数可以在类内定义
void push(const T& value) {
elements.push_back(value);
}
void push(T&& value) {
elements.push_back(std::move(value));
}
T pop() {
if (elements.empty()) {
throw std::out_of_range("Stack<>::pop(): empty stack");
}
T value = std::move(elements.back());
elements.pop_back();
return value;
}
const T& top() const {
if (elements.empty()) {
throw std::out_of_range("Stack<>::top(): empty stack");
}
return elements.back();
}
bool empty() const {
return elements.empty();
}
std::size_t size() const {
return elements.size();
}
};
// 类外定义成员函数
template<typename T>
void Stack<T>::clear() {
elements.clear();
}
void class_template_demo() {
Stack<int> intStack;
intStack.push(1);
intStack.push(2);
intStack.push(3);
std::cout << "Top: " << intStack.top() << std::endl;
std::cout << "Size: " << intStack.size() << std::endl;
while (!intStack.empty()) {
std::cout << intStack.pop() << " ";
}
std::cout << std::endl;
}
模板友元
template<typename T>
class Box {
private:
T value;
public:
Box(const T& v) : value(v) {}
// 声明友元函数模板
template<typename U>
friend std::ostream& operator<<(std::ostream& os, const Box<U>& box);
};
// 定义友元函数模板
template<typename U>
std::ostream& operator<<(std::ostream& os, const Box<U>& box) {
return os << "Box(" << box.value << ")";
}
3. 模板特化和偏特化
完全特化
// 主模板
template<typename T>
class TypeInfo {
public:
static std::string name() {
return "unknown";
}
};
// 完全特化
template<>
class TypeInfo<int> {
public:
static std::string name() {
return "int";
}
};
template<>
class TypeInfo<double> {
public:
static std::string name() {
return "double";
}
};
template<>
class TypeInfo<std::string> {
public:
static std::string name() {
return "std::string";
}
};
偏特化(部分特化)
// 主模板
template<typename T, typename U>
class Pair {
public:
static std::string name() {
return "Pair<T, U>";
}
};
// 偏特化:两个类型相同
template<typename T>
class Pair<T, T> {
public:
static std::string name() {
return "Pair<T, T>";
}
};
// 偏特化:第二个类型为 int
template<typename T>
class Pair<T, int> {
public:
static std::string name() {
return "Pair<T, int>";
}
};
// 偏特化:指针类型
template<typename T, typename U>
class Pair<T*, U*> {
public:
static std::string name() {
return "Pair<T*, U*>";
}
};
void specialization_demo() {
std::cout << TypeInfo<int>::name() << std::endl; // int
std::cout << TypeInfo<double>::name() << std::endl; // double
std::cout << TypeInfo<std::string>::name() << std::endl; // std::string
std::cout << Pair<int, double>::name() << std::endl; // Pair<T, U>
std::cout << Pair<int, int>::name() << std::endl; // Pair<T, T>
std::cout << Pair<double, int>::name() << std::endl; // Pair<T, int>
std::cout << Pair<int*, double*>::name() << std::endl; // Pair<T*, U*>
}
4. 模板元编程
编译时计算
// 编译时阶乘计算
template<unsigned n>
struct Factorial {
static constexpr unsigned value = n * Factorial<n - 1>::value;
};
// 基况特化
template<>
struct Factorial<0> {
static constexpr unsigned value = 1;
};
// 编译时判断类型是否相同
template<typename T, typename U>
struct IsSame {
static constexpr bool value = false;
};
template<typename T>
struct IsSame<T, T> {
static constexpr bool value = true;
};
void template_metaprogramming_demo() {
std::cout << "Factorial<5>::value = " << Factorial<5>::value << std::endl; // 120
std::cout << "Factorial<10>::value = " << Factorial<10>::value << std::endl; // 3628800
std::cout << std::boolalpha;
std::cout << "IsSame<int, int>::value = " << IsSame<int, int>::value << std::endl; // true
std::cout << "IsSame<int, double>::value = " << IsSame<int, double>::value << std::endl; // false
}
类型特征(Type Traits)
#include <type_traits>
// 自定义类型特征
template<typename T>
struct IsPointer {
static constexpr bool value = false;
};
template<typename T>
struct IsPointer<T*> {
static constexpr bool value = true;
};
template<typename T>
void process(const T& value) {
if constexpr (IsPointer<T>::value) {
std::cout << "Pointer: " << *value << std::endl;
} else {
std::cout << "Value: " << value << std::endl;
}
}
void type_traits_demo() {
int x = 42;
int* ptr = &x;
process(x); // Value: 42
process(ptr); // Pointer: 42
// 标准库类型特征
std::cout << "is_integral<int>: " << std::is_integral<int>::value << std::endl;
std::cout << "is_floating_point<double>: " << std::is_floating_point<double>::value << std::endl;
std::cout << "is_pointer<int*>: " << std::is_pointer<int*>::value << std::endl;
}
5. 可变参数模板
参数包和递归展开
// 基况函数
void print() {
std::cout << std::endl;
}
// 可变参数模板函数
template<typename T, typename... Args>
void print(T first, Args... args) {
std::cout << first;
if constexpr (sizeof...(args) > 0) {
std::cout << ", ";
}
print(args...); // 递归调用
}
// 使用折叠表达式(C++17)
template<typename... Args>
void print_fold(Args... args) {
((std::cout << args << ", "), ...) << std::endl;
}
// 计算参数包大小
template<typename... Args>
constexpr std::size_t count_args(Args... args) {
return sizeof...(args);
}
void variadic_template_demo() {
print(1, 2.5, "hello", 'a'); // 1, 2.5, hello, a
print_fold(1, 2.5, "hello", 'a'); // 1, 2.5, hello, a,
std::cout << "count_args(1, 2, 3): " << count_args(1, 2, 3) << std::endl; // 3
std::cout << "count_args(): " << count_args() << std::endl; // 0
}
完美转发
// 工厂函数模板
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
class Widget {
private:
std::string name;
int value;
public:
Widget(const std::string& n, int v) : name(n), value(v) {
std::cout << "Widget constructed: " << name << ", " << value << std::endl;
}
Widget(std::string&& n, int v) : name(std::move(n)), value(v) {
std::cout << "Widget move constructed: " << name << ", " << value << std::endl;
}
};
void perfect_forwarding_demo() {
std::string name = "test";
// 完美转发:保持值类别
auto w1 = make_unique<Widget>(name, 42); // 拷贝 name
auto w2 = make_unique<Widget>(std::move(name), 42); // 移动 name
auto w3 = make_unique<Widget>("temporary", 42); // 使用临时对象
}
6. 模板高级特性
SFINAE(替换失败不是错误)
// SFINAE:根据类型是否有特定成员函数选择不同实现
template<typename T>
class HasSize {
private:
template<typename U>
static auto test(int) -> decltype(std::declval<U>().size(), std::true_type{});
template<typename U>
static std::false_type test(...);
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
template<typename Container>
typename std::enable_if<HasSize<Container>::value, std::size_t>::type
get_size(const Container& c) {
return c.size(); // 如果容器有 size() 成员函数
}
template<typename Container>
typename std::enable_if<!HasSize<Container>::value, std::size_t>::type
get_size(const Container& c) {
return 0; // 默认实现
}
void sfinae_demo() {
std::vector<int> vec = {1, 2, 3};
std::cout << "vector size: " << get_size(vec) << std::endl; // 3
int arr[] = {1, 2, 3};
std::cout << "array size: " << get_size(arr) << std::endl; // 0
}
概念(C++20)
#ifdef __cpp_concepts
// C++20 概念
template<typename T>
concept Arithmetic = std::is_arithmetic_v<T>;
template<typename T>
concept HasArea = requires(T t) {
t.area();
};
template<Arithmetic T>
T square(const T& x) {
return x * x;
}
template<HasArea T>
void print_area(const T& shape) {
std::cout << "Area: " << shape.area() << std::endl;
}
class Circle {
public:
double area() const { return 3.14159 * radius * radius; }
double radius = 1.0;
};
void concepts_demo() {
std::cout << square(5) << std::endl; // 25
std::cout << square(3.14) << std::endl; // 9.8596
// square("hello"); // 错误:不满足 Arithmetic 概念
Circle c;
print_area(c); // Area: 3.14159
}
#endif
7. 实践项目:智能指针模板
template<typename T>
class UniquePtr {
private:
T* ptr;
public:
// 构造函数
explicit UniquePtr(T* p = nullptr) : ptr(p) {}
// 禁止拷贝
UniquePtr(const UniquePtr&) = delete;
UniquePtr& operator=(const UniquePtr&) = delete;
// 移动语义
UniquePtr(UniquePtr&& other) noexcept : ptr(other.ptr) {
other.ptr = nullptr;
}
UniquePtr& operator=(UniquePtr&& other) noexcept {
if (this != &other) {
delete ptr;
ptr = other.ptr;
other.ptr = nullptr;
}
return *this;
}
// 析构函数
~UniquePtr() {
delete ptr;
}
// 操作符重载
T& operator*() const { return *ptr; }
T* operator->() const { return ptr; }
explicit operator bool() const { return ptr != nullptr; }
// 资源管理
T* release() {
T* temp = ptr;
ptr = nullptr;
return temp;
}
void reset(T* p = nullptr) {
delete ptr;
ptr = p;
}
T* get() const { return ptr; }
};
// 特化版本用于数组
template<typename T>
class UniquePtr<T[]> {
private:
T* ptr;
public:
explicit UniquePtr(T* p = nullptr) : ptr(p) {}
~UniquePtr() {
delete[] ptr;
}
// 禁止拷贝,允许移动(与上面类似)
// ...
T& operator[](std::size_t index) const {
return ptr[index];
}
};
最佳实践总结
- 使用 typename 和 class 声明类型参数(两者等价)
- 模板定义通常放在头文件中
- 使用 SFINAE 或概念进行约束
- 优先使用可变参数模板而不是函数重载
- 理解模板实例化的时机和成本
- 使用特化提供特定类型的优化实现
调试技巧
// 在模板中添加静态断言进行编译时检查
template<typename T>
void process(const T& value) {
static_assert(std::is_arithmetic_v<T>,
"T must be an arithmetic type");
// ...
}
1506

被折叠的 条评论
为什么被折叠?



