C++ Primer 第16章:模板与泛型编程
16.1 定义模板
16.1.1 函数模板
模板的核心思想:
┌─────────────────────────────────────────────────────┐
│ 泛型编程(Generic Programming) │
│ │
│ 不依赖具体类型,编写通用代码 │
│ 编译器根据调用时的类型自动生成具体版本 │
│ │
│ template <typename T> │
│ T max(T a, T b) { return a > b ? a : b; } │
│ │
│ max(3, 5) → 生成 int 版本 │
│ max(3.14, 2.7) → 生成 double 版本 │
│ max("a", "b") → 生成 string 版本 │
└─────────────────────────────────────────────────────┘
// function_template.cpp -- 函数模板
#include <iostream>
#include <string>
#include <vector>
// ===== 基本函数模板 =====
template <typename T>
T myMax(T a, T b)
{
return (a > b) ? a : b;
}
// ===== 多类型参数 =====
template <typename T1, typename T2>
auto add(T1 a, T2 b) -> decltype(a + b)
{
return a + b;
}
// ===== 非类型模板参数 =====
template <typename T, int N>
void printArray(T (&arr)[N]) // 数组引用,保留大小信息
{
for (int i = 0; i < N; i++)
std::cout << arr[i] << " ";
std::cout << std::endl;
}
// ===== 模板函数的实例化 =====
// 显式实例化:template int myMax<int>(int, int);
// 隐式实例化:编译器自动推断
int main()
{
using namespace std;
// 隐式实例化(编译器推断类型)
cout << myMax(3, 5) << endl; // int 版本
cout << myMax(3.14, 2.71) << endl; // double 版本
cout << myMax('a', 'z') << endl; // char 版本
cout << myMax(string("apple"), string("banana")) << endl;
// 显式指定类型
cout << myMax<double>(3, 5.5) << endl; // 强制使用 double
// 多类型参数
cout << add(3, 4.5) << endl; // int + double = double
cout << add(1.5, 2.5) << endl; // double + double = double
// 非类型参数
int iarr[] = {1, 2, 3, 4, 5};
double darr[] = {1.1, 2.2, 3.3};
printArray(iarr); // N=5 自动推断
printArray(darr); // N=3 自动推断
return 0;
}
16.1.2 类模板
// class_template.cpp -- 类模板
#include <iostream>
#include <stdexcept>
#include <string>
// ===== 基本类模板 =====
template <typename T>
class Stack
{
public:
Stack() = default;
void push(const T& val)
{
data_.push_back(val);
}
void push(T&& val) // 移动版本
{
data_.push_back(std::move(val));
}
T pop()
{
if (empty()) throw std::underflow_error("栈为空");
T val = std::move(data_.back());
data_.pop_back();
return val;
}
const T& top() const
{
if (empty()) throw std::underflow_error("栈为空");
return data_.back();
}
bool empty() const { return data_.empty(); }
size_t size() const { return data_.size(); }
// 成员函数模板
template <typename U>
void pushAll(const Stack<U>& other)
{
for (const auto& val : other.data_)
push(static_cast<T>(val));
}
// 友元声明
template <typename U>
friend std::ostream& operator<<(std::ostream& os, const Stack<U>& s);
private:
std::vector<T> data_;
};
template <typename T>
std::ostream& operator<<(std::ostream& os, const Stack<T>& s)
{
os << "[";
for (size_t i = 0; i < s.data_.size(); i++)
{
os << s.data_[i];
if (i < s.data_.size() - 1) os << ", ";
}
os << "]";
return os;
}
int main()
{
using namespace std;
// 使用类模板(必须显式指定类型)
Stack<int> intStack;
intStack.push(1);
intStack.push(2);
intStack.push(3);
cout << "intStack: " << intStack << endl;
cout << "top: " << intStack.top() << endl;
cout << "pop: " << intStack.pop() << endl;
cout << "intStack: " << intStack << endl;
Stack<string> strStack;
strStack.push("Hello");
strStack.push("World");
cout << "strStack: " << strStack << endl;
return 0;
}
16.1.3 模板参数
// template_params.cpp -- 模板参数详解
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
// ===== 类型参数 =====
template <typename T>
T identity(T val) { return val; }
// ===== 非类型参数 =====
// 非类型参数必须是常量表达式
template <int N>
int addN(int val) { return val + N; }
template <typename T, size_t N>
class FixedArray
{
public:
T& operator[](size_t i) { return data_[i]; }
const T& operator[](size_t i) const { return data_[i]; }
size_t size() const { return N; }
void fill(const T& val)
{
for (auto& x : data_) x = val;
}
void show() const
{
for (const auto& x : data_) std::cout << x << " ";
std::cout << std::endl;
}
private:
T data_[N];
};
// ===== 模板模板参数 =====
template <typename T, template <typename> class Container>
class Wrapper
{
public:
void add(const T& val) { data_.push_back(val); }
size_t size() const { return data_.size(); }
private:
Container<T> data_;
};
// ===== 默认模板参数 =====
template <typename T, typename Compare = std::less<T>>
bool isOrdered(const std::vector<T>& v, Compare cmp = Compare())
{
for (size_t i = 1; i < v.size(); i++)
if (!cmp(v[i-1], v[i])) return false;
return true;
}
int main()
{
using namespace std;
// 非类型参数
cout << addN<5>(10) << endl; // 15
cout << addN<-3>(10) << endl; // 7
// 固定大小数组
FixedArray<int, 5> arr;
arr.fill(42);
arr.show();
FixedArray<double, 3> darr;
darr[0] = 1.1; darr[1] = 2.2; darr[2] = 3.3;
darr.show();
// 默认模板参数
vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {5, 4, 3, 2, 1};
cout << boolalpha;
cout << "v1升序:" << isOrdered(v1) << endl; // true
cout << "v2升序:" << isOrdered(v2) << endl; // false
cout << "v2降序:" << isOrdered(v2, greater<int>()) << endl; // true
return 0;
}
16.2 模板实参推断
// template_deduction.cpp -- 模板实参推断
#include <iostream>
#include <string>
// ===== 基本推断规则 =====
template <typename T>
void func(T val) { std::cout << "T=" << typeid(T).name() << std::endl; }
template <typename T>
void funcRef(T& val) { std::cout << "T&" << std::endl; }
template <typename T>
void funcConstRef(const T& val) { std::cout << "const T&" << std::endl; }
template <typename T>
void funcRRef(T&& val) { std::cout << "T&&(万能引用)" << std::endl; }
// ===== 引用折叠规则 =====
// T& & → T&
// T& && → T&
// T&& & → T&
// T&& && → T&&
// ===== 完美转发 =====
template <typename T>
void wrapper(T&& arg)
{
// std::forward 保持参数的值类别
inner(std::forward<T>(arg));
}
void inner(int& x) { std::cout << "左值引用" << std::endl; }
void inner(int&& x) { std::cout << "右值引用" << std::endl; }
int main()
{
using namespace std;
int x = 42;
const int cx = 42;
// 传值:忽略顶层const和引用
func(x); // T = int
func(cx); // T = int(忽略const)
func(42); // T = int
// 传引用:保留const
funcRef(x); // T = int
// funcRef(42); // ❌ 不能绑定右值到非const引用
funcConstRef(x); // T = int
funcConstRef(cx); // T = int
funcConstRef(42); // T = int(可以绑定右值)
// 万能引用(universal reference)
funcRRef(x); // T = int&(左值,引用折叠为T&)
funcRRef(42); // T = int(右值,T&&)
// 完美转发
wrapper(x); // 转发左值
wrapper(42); // 转发右值
return 0;
}
16.3 重载与模板
// template_overload.cpp -- 模板重载
#include <iostream>
#include <string>
#include <cstring>
// 通用模板
template <typename T>
std::string debug_rep(const T& t)
{
std::ostringstream ret;
ret << t;
return ret.str();
}
// 指针版本
template <typename T>
std::string debug_rep(T* p)
{
std::ostringstream ret;
ret << "pointer: " << p;
if (p)
ret << " " << debug_rep(*p);
else
ret << " null pointer";
return ret.str();
}
// const char* 特化(非模板函数,优先级最高)
std::string debug_rep(const char* p)
{
return debug_rep(std::string(p));
}
// 函数模板的匹配规则:
// 1. 精确匹配的非模板函数(最高优先级)
// 2. 精确匹配的模板函数
// 3. 需要类型转换的非模板函数
int main()
{
using namespace std;
int i = 42;
cout << debug_rep(i) << endl; // 通用模板
cout << debug_rep(&i) << endl; // 指针模板
cout << debug_rep("hello") << endl; // const char* 版本
return 0;
}
16.4 可变参数模板
// variadic_template.cpp -- 可变参数模板
#include <iostream>
#include <string>
#include <sstream>
// ===== 基本可变参数模板 =====
// 递归终止函数
void print() { std::cout << std::endl; }
// 可变参数模板
template <typename T, typename... Args>
void print(T first, Args... rest)
{
std::cout << first;
if (sizeof...(rest) > 0) std::cout << ", ";
print(rest...); // 递归展开
}
// ===== sizeof... 运算符 =====
template <typename... Args>
void countArgs(Args... args)
{
std::cout << "参数个数:" << sizeof...(args) << std::endl;
}
// ===== 完美转发的可变参数 =====
template <typename T, typename... Args>
T* create(Args&&... args)
{
return new T(std::forward<Args>(args)...);
}
// ===== 折叠表达式(C++17)=====
template <typename... Args>
auto sum(Args... args)
{
return (args + ...); // 一元右折叠
}
template <typename... Args>
auto product(Args... args)
{
return (args * ...);
}
// ===== 实际应用:类型安全的 printf =====
void myPrintf(const char* fmt)
{
while (*fmt)
{
if (*fmt == '%' && *(fmt+1) != '%')
throw std::runtime_error("参数不足");
std::cout << *fmt++;
}
}
template <typename T, typename... Args>
void myPrintf(const char* fmt, T val, Args... args)
{
while (*fmt)
{
if (*fmt == '%' && *(fmt+1) != '%')
{
std::cout << val;
myPrintf(fmt + 2, args...);
return;
}
std::cout << *fmt++;
}
}
int main()
{
using namespace std;
// 可变参数打印
print(1, 2.5, "Hello", 'A', true);
// 参数个数
countArgs(1, 2, 3);
countArgs("a", "b");
countArgs();
// 折叠表达式
cout << "sum(1,2,3,4,5) = " << sum(1, 2, 3, 4, 5) << endl;
cout << "product(1,2,3,4) = " << product(1, 2, 3, 4) << endl;
// 类型安全的printf
myPrintf("Hello, %! You are % years old.\n", "Alice", 25);
return 0;
}
16.5 模板特化
16.5.1 函数模板特化
// function_specialization.cpp -- 函数模板特化
#include <iostream>
#include <cstring>
#include <string>
// 通用模板
template <typename T>
bool isEqual(const T& a, const T& b)
{
return a == b;
}
// 针对 const char* 的完全特化
template <>
bool isEqual<const char*>(const char* const& a, const char* const& b)
{
return strcmp(a, b) == 0;
}
// 通用 hash 模板
template <typename T>
struct Hash
{
size_t operator()(const T& val) const
{
return std::hash<T>{}(val);
}
};
// 针对 pair<int,int> 的特化
template <>
struct Hash<std::pair<int, int>>
{
size_t operator()(const std::pair<int, int>& p) const
{
size_t h1 = std::hash<int>{}(p.first);
size_t h2 = std::hash<int>{}(p.second);
return h1 ^ (h2 << 1);
}
};
int main()
{
using namespace std;
cout << boolalpha;
cout << isEqual(1, 1) << endl; // true(通用版本)
cout << isEqual(1.0, 1.0) << endl; // true
cout << isEqual("abc", "abc") << endl; // true(特化版本)
cout << isEqual("abc", "def") << endl; // false
Hash<int> h1;
Hash<pair<int,int>> h2;
cout << "hash(42) = " << h1(42) << endl;
cout << "hash({1,2}) = " << h2({1, 2}) << endl;
return 0;
}
16.5.2 类模板特化
// class_specialization.cpp -- 类模板特化
#include <iostream>
#include <string>
#include <cstring>
// 通用模板
template <typename T>
class TypeTraits
{
public:
static const bool isPointer = false;
static const bool isIntegral = false;
static std::string name() { return "unknown"; }
};
// 完全特化:针对 int
template <>
class TypeTraits<int>
{
public:
static const bool isPointer = false;
static const bool isIntegral = true;
static std::string name() { return "int"; }
};
// 完全特化:针对 double
template <>
class TypeTraits<double>
{
public:
static const bool isPointer = false;
static const bool isIntegral = false;
static std::string name() { return "double"; }
};
// 部分特化:针对指针类型
template <typename T>
class TypeTraits<T*>
{
public:
static const bool isPointer = true;
static const bool isIntegral = false;
static std::string name() { return TypeTraits<T>::name() + "*"; }
};
// 部分特化:针对 const 类型
template <typename T>
class TypeTraits<const T>
{
public:
static const bool isPointer = TypeTraits<T>::isPointer;
static const bool isIntegral = TypeTraits<T>::isIntegral;
static std::string name() { return "const " + TypeTraits<T>::name(); }
};
// ===== 实际应用:类型安全的容器 =====
template <typename T>
class SafeContainer
{
public:
void add(const T& val) { data_.push_back(val); }
T get(size_t i) const { return data_[i]; }
size_t size() const { return data_.size(); }
private:
std::vector<T> data_;
};
// 针对 bool 的特化(节省空间)
template <>
class SafeContainer<bool>
{
public:
void add(bool val) { data_.push_back(val); }
bool get(size_t i) const { return data_[i]; }
size_t size() const { return data_.size(); }
private:
std::vector<char> data_; // 用 char 存储 bool,节省空间
};
int main()
{
using namespace std;
cout << boolalpha;
cout << "int: " << TypeTraits<int>::name()
<< " isIntegral=" << TypeTraits<int>::isIntegral << endl;
cout << "double: " << TypeTraits<double>::name() << endl;
cout << "int*: " << TypeTraits<int*>::name()
<< " isPointer=" << TypeTraits<int*>::isPointer << endl;
cout << "const int: " << TypeTraits<const int>::name() << endl;
cout << "const int*: " << TypeTraits<const int*>::name() << endl;
return 0;
}
16.6 模板与继承
// template_inheritance.cpp -- 模板与继承
#include <iostream>
#include <string>
#include <vector>
// ===== 模板基类 =====
template <typename T>
class Container
{
public:
virtual void add(const T& val) = 0;
virtual T get(size_t i) const = 0;
virtual size_t size() const = 0;
virtual ~Container() {}
// 非虚成员函数
bool empty() const { return size() == 0; }
void showAll() const
{
for (size_t i = 0; i < size(); i++)
std::cout << get(i) << " ";
std::cout << std::endl;
}
};
// ===== 派生类模板 =====
template <typename T>
class VectorContainer : public Container<T>
{
public:
void add(const T& val) override { data_.push_back(val); }
T get(size_t i) const override { return data_[i]; }
size_t size() const override { return data_.size(); }
private:
std::vector<T> data_;
};
template <typename T>
class SortedContainer : public Container<T>
{
public:
void add(const T& val) override
{
auto it = std::lower_bound(data_.begin(), data_.end(), val);
data_.insert(it, val);
}
T get(size_t i) const override { return data_[i]; }
size_t size() const override { return data_.size(); }
private:
std::vector<T> data_;
};
// ===== CRTP(奇异递归模板模式)=====
// 在编译时实现多态,避免虚函数开销
template <typename Derived>
class Base
{
public:
void interface()
{
static_cast<Derived*>(this)->implementation();
}
static void staticInterface()
{
Derived::staticImplementation();
}
};
class ConcreteA : public Base<ConcreteA>
{
public:
void implementation()
{
std::cout << "ConcreteA::implementation" << std::endl;
}
static void staticImplementation()
{
std::cout << "ConcreteA::staticImplementation" << std::endl;
}
};
class ConcreteB : public Base<ConcreteB>
{
public:
void implementation()
{
std::cout << "ConcreteB::implementation" << std::endl;
}
static void staticImplementation()
{
std::cout << "ConcreteB::staticImplementation" << std::endl;
}
};
int main()
{
using namespace std;
// 模板继承
VectorContainer<int> vc;
vc.add(3); vc.add(1); vc.add(4); vc.add(1); vc.add(5);
cout << "VectorContainer: "; vc.showAll();
SortedContainer<int> sc;
sc.add(3); sc.add(1); sc.add(4); sc.add(1); sc.add(5);
cout << "SortedContainer: "; sc.showAll();
// 多态
Container<int>* p = ≻
p->showAll();
// CRTP
ConcreteA a;
ConcreteB b;
a.interface();
b.interface();
ConcreteA::staticInterface();
return 0;
}
16.7 综合示例:通用算法库
// generic_algorithms.cpp -- 综合示例:通用算法库
#include <iostream>
#include <vector>
#include <list>
#include <string>
#include <algorithm>
#include <functional>
#include <numeric>
#include <type_traits>
#include <sstream>
// ===== 通用打印 =====
template <typename Container>
void printContainer(const Container& c, const std::string& label = "")
{
if (!label.empty()) std::cout << label << ": ";
for (const auto& x : c) std::cout << x << " ";
std::cout << std::endl;
}
// ===== 通用查找 =====
template <typename Container, typename T>
auto findFirst(const Container& c, const T& val)
-> decltype(std::begin(c))
{
return std::find(std::begin(c), std::end(c), val);
}
// ===== 通用变换 =====
template <typename Container, typename Func>
auto transform_all(const Container& c, Func f)
-> std::vector<decltype(f(*std::begin(c)))>
{
using ResultType = decltype(f(*std::begin(c)));
std::vector<ResultType> result;
result.reserve(std::distance(std::begin(c), std::end(c)));
for (const auto& x : c)
result.push_back(f(x));
return result;
}
// ===== 通用过滤 =====
template <typename Container, typename Pred>
Container filter_all(const Container& c, Pred pred)
{
Container result;
std::copy_if(std::begin(c), std::end(c),
std::back_inserter(result), pred);
return result;
}
// ===== 通用归约 =====
template <typename Container, typename T, typename BinaryOp>
T reduce_all(const Container& c, T init, BinaryOp op)
{
return std::accumulate(std::begin(c), std::end(c), init, op);
}
// ===== 类型特征检查 =====
template <typename T>
void typeInfo()
{
using namespace std;
cout << "类型信息:" << endl;
cout << " is_integral: " << boolalpha << is_integral<T>::value << endl;
cout << " is_floating: " << is_floating_point<T>::value << endl;
cout << " is_pointer: " << is_pointer<T>::value << endl;
cout << " is_reference: " << is_reference<T>::value << endl;
}
// ===== 编译时条件(SFINAE)=====
// 只对整数类型启用
template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
safeDiv(T a, T b)
{
if (b == 0) throw std::invalid_argument("除数不能为0");
return a / b;
}
// 只对浮点类型启用
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type
safeDiv(T a, T b)
{
if (b == 0.0) return std::numeric_limits<T>::infinity();
return a / b;
}
int main()
{
using namespace std;
// 通用打印
vector<int> vi = {1, 2, 3, 4, 5};
list<string> ls = {"Hello", "World", "C++"};
printContainer(vi, "vector<int>");
printContainer(ls, "list<string>");
// 通用查找
auto it = findFirst(vi, 3);
if (it != vi.end())
cout << "找到3,位置:" << distance(vi.begin(), it) << endl;
// 通用变换
auto doubled = transform_all(vi, [](int x) { return x * 2; });
printContainer(doubled, "翻倍");
auto lengths = transform_all(ls, [](const string& s) { return s.size(); });
printContainer(lengths, "字符串长度");
// 通用过滤
auto evens = filter_all(vi, [](int x) { return x % 2 == 0; });
printContainer(evens, "偶数");
// 通用归约
int sum = reduce_all(vi, 0, plus<int>());
int product = reduce_all(vi, 1, multiplies<int>());
cout << "sum=" << sum << " product=" << product << endl;
// 类型特征
cout << "\nint的类型信息:" << endl;
typeInfo<int>();
cout << "\ndouble的类型信息:" << endl;
typeInfo<double>();
// SFINAE
cout << "\nSFINAE示例:" << endl;
cout << "safeDiv(10, 3) = " << safeDiv(10, 3) << endl;
cout << "safeDiv(10.0, 3.0) = " << safeDiv(10.0, 3.0) << endl;
cout << "safeDiv(1.0, 0.0) = " << safeDiv(1.0, 0.0) << endl;
return 0;
}
📝 第16章知识点总结
| 知识点 |
核心要点 |
| 函数模板 |
template <typename T> 定义,编译器根据调用自动实例化 |
| 类模板 |
必须显式指定类型参数,成员函数在类外定义需要 template <typename T> |
| 非类型参数 |
整数、指针等常量表达式,如 template <int N> |
| 默认模板参数 |
template <typename T, typename Compare = std::less<T>> |
| 模板实参推断 |
编译器从函数调用推断类型,传值忽略顶层const |
| 万能引用 |
T&& 在模板中是万能引用,可绑定左值和右值 |
| 引用折叠 |
T& && → T&,T&& && → T&& |
| 完美转发 |
std::forward<T>(arg) 保持参数的值类别 |
| 可变参数模板 |
typename... Args,sizeof...(args) 获取数量,递归展开 |
| 折叠表达式 |
C++17,(args + ...) 一元折叠,(init + ... + args) 二元折叠 |
| 函数模板特化 |
template <> 为特定类型提供专门实现 |
| 类模板完全特化 |
template <> class Foo<int> 针对特定类型 |
| 类模板部分特化 |
template <typename T> class Foo<T*> 针对类型模式 |
| SFINAE |
enable_if 根据类型特征启用/禁用模板 |
| CRTP |
奇异递归模板模式,编译时多态,避免虚函数开销 |