【Linux C/C++开发】第12章:模板编程 - 类型无关的代码魔法
第12章:模板编程 - 类型无关的代码魔法
12.1 模板编程基础概念
12.1.1 模板的设计哲学
想象你是一家制造工厂,需要生产不同型号的螺丝。你不会为每种型号都建一条生产线,而是设计一条可调节的生产线,通过更换模具来生产不同型号的产品。C++模板就是程序员的"可调节生产线"。
核心思想:
- 类型参数化:把类型当作参数传递,就像把模具规格当作参数
- 编译时实例化:在编译时根据具体类型生成代码,就像在生产前根据规格调整机器
- 零开销抽象:不会增加运行时开销,就像调整机器不会增加材料成本
- 类型安全:保持C++的强类型检查,就像质检系统确保产品质量
实际意义:你可以写一个排序函数,它能排序整数数组、字符串数组、自定义对象数组,而不需要为每种类型都写一遍代码。
12.1.2 模板的分类
函数模板:通用的函数配方
template<typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
// 使用
int a = max(3, 5); // 自动推导为max<int>
double b = max(3.14, 2.71); // 自动推导为max<double>
类模板:通用的类配方
template<typename T>
class Stack {
std::vector<T> elements;
public:
void push(T const& elem) { elements.push_back(elem); }
void pop() {
if (!elements.empty()) elements.pop_back();
}
T top() const {
if (!elements.empty()) return elements.back();
throw std::runtime_error("Stack is empty!");
}
bool empty() const { return elements.empty(); }
};
// 使用
Stack<int> intStack; // 存储整数的栈
Stack<std::string> strStack; // 存储字符串的栈
变量模板(C++14):通用的常量配方
template<typename T>
constexpr T pi = T(3.14159265358979323846);
// 使用
float pi_f = pi<float>; // float类型的π
double pi_d = pi<double>; // double类型的π
别名模板:通用的类型别名
template<typename T>
using Vec = std::vector<T>;
// 使用
Vec<int> numbers; // 等价于std::vector<int>
Vec<std::string> names; // 等价于std::vector<std::string>
12.1.3 模板参数的种类
类型参数:最常用,代表一个类型
template<typename T> // 或template<class T>
class MyClass {
T value;
public:
void setValue(T val) { value = val; }
T getValue() const { return value; }
};
非类型参数:代表一个常量值
template<int N>
class Array {
int data[N]; // 编译时已知大小的数组
public:
int size() const { return N; }
int& operator[](int index) {
if (index >= N) throw std::out_of_range("Index too large");
return data[index];
}
};
// 使用
Array<10> smallArray; // 大小为10的数组
Array<1000> largeArray; // 大小为1000的数组
模板模板参数:代表一个模板
template<typename T, template<typename> class Container>
class Adapter {
Container<T> storage;
public:
void add(const T& item) { storage.push_back(item); }
T get(size_t index) const { return storage[index]; }
};
// 使用
Adapter<int, std::vector> intAdapter; // 使用vector作为底层容器
参数包:支持可变数量的参数
template<typename... Args>
void print(Args... args) {
((std::cout << args << " "), ...); // C++17折叠表达式
std::cout << std::endl;
}
// 使用
print(1, 2.5, "hello", 'c'); // 可以传任意数量和类型的参数
12.2 模板实例化机制
12.2.1 两阶段编译检查
模板编译就像定制家具:
- 设计图纸检查:检查模板本身的语法是否正确(就像检查设计图是否合理)
- 实际制作检查:在根据具体尺寸制作时,检查是否可行(就像检查木材是否足够结实)
template<typename T>
void func(T t) {
t.method(); // 这里不会立即检查method是否存在
}
struct MyClass {
void method() { std::cout << "MyClass method\n"; }
};
struct AnotherClass {
// 没有method方法
};
func(MyClass{}); // ✅ 可以编译,MyClass有method方法
// func(AnotherClass{}); // ❌ 编译错误,AnotherClass没有method方法
12.2.2 隐式实例化与显式实例化
隐式实例化:用的时候自动生成
Stack<int> intStack; // 编译器自动实例化Stack<int>
Stack<double> doubleStack; // 编译器自动实例化Stack<double>
显式实例化:提前告诉编译器生成
// 告诉编译器:请提前生成这些版本的代码
template class Stack<int>;
template class Stack<double>;
template int max<int>(int, int);
显式实例化声明(C++11):告诉编译器"别处会提供"
// 头文件中声明:别处会提供这个实例化
extern template class Stack<int>;
// 源文件中提供实际实例化
template class Stack<int>;
12.2.3 避免代码膨胀
代码膨胀问题:每种模板实例化都会生成独立的机器码,就像为每种产品都建一条完整的生产线。
优化策略:
- 显式实例化控制:集中管理实例化位置
// 只在源文件中实例化常用版本
// Stack.cpp
template class Stack<int>;
template class Stack<double>;
template class Stack<std::string>;
- 代码因式分解:把通用部分提取出来
// 通用基类:不依赖模板参数
class StackBase {
protected:
void checkEmpty() const {
if (empty()) throw std::runtime_error("Stack is empty!");
}
};
// 模板派生类:只包含类型相关的代码
template<typename T>
class Stack : private StackBase {
std::vector<T> elements;
public:
void push(const T& elem) { elements.push_back(elem); }
void pop() {
checkEmpty(); // 使用基类的通用检查
elements.pop_back();
}
T& top() {
checkEmpty(); // 使用基类的通用检查
return elements.back();
}
};
12.3 模板特化 - 特殊情况特殊处理
12.3.1 全特化 - 完全定制
全特化就像为特殊客户定制产品:当通用模板不适合某些类型时,提供专门的实现。
// 通用模板
template<typename T>
class ArrayComparator {
public:
bool compare(const T& a, const T& b) const {
return a < b; // 默认使用小于比较
}
};
// 为const char*类型特化:比较字符串内容而不是指针
template<>
class ArrayComparator<const char*> {
public:
bool compare(const char* a, const char* b) const {
return std::strcmp(a, b) < 0;
}
};
12.3.2 偏特化 - 部分定制
偏特化就像为某类产品定制:只对部分参数进行特化。
// 通用模板
template<typename T, typename U>
class Pair {
T first;
U second;
public:
void print() const {
std::cout << "Generic: " << first << ", " << second << std::endl;
}
};
// 偏特化:两个类型相同时的特殊处理
template<typename T>
class Pair<T, T> {
T first;
T second;
public:
void print() const {
std::cout << "Same type pair: " << first << " and " << second << std::endl;
}
T sum() const { return first + second; } // 只有同类型才能相加
};
// 偏特化:指针类型的特殊处理
template<typename T, typename U>
class Pair<T*, U*> {
T* first;
U* second;
public:
void print() const {
std::cout << "Pointer pair: " << (first ? *first : 0)
<< ", " << (second ? *second : 0) << std::endl;
}
};
12.3.3 函数模板特化
函数模板特化的限制:函数模板只支持全特化,不支持偏特化。但可以通过重载实现类似效果。
// 通用函数模板
template<typename T>
std::string type_name() {
return "Unknown type";
}
// 全特化:为特定类型提供专门实现
template<>
std::string type_name<int>() {
return "int";
}
template<>
std::string type_name<double>() {
return "double";
}
// 函数重载:实现类似偏特化的效果
std::string type_name(const std::string&) {
return "std::string";
}
12.4 SFINAE - 替换失败不是错误
12.4.1 SFINAE的基本原理
SFINAE就像求职筛选:当某个候选人不满足要求时,不是报错,而是继续看下一个候选人。
// 检查类型是否有size()方法
template<typename T>
auto get_size(const T& t) -> decltype(t.size(), size_t()) {
return t.size(); // 如果T有size()方法,使用这个版本
}
// 通用版本
template<typename T>
size_t get_size(const T&) {
return sizeof(T); // 如果T没有size()方法,使用这个版本
}
12.4.2 enable_if的使用
enable_if就像智能门禁:只有满足条件的人才能通过。
// 只在T是整数类型时启用这个函数
template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
multiply_by_two(T value) {
return value * 2;
}
// 只在T是浮点类型时启用这个函数
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type
multiply_by_two(T value) {
return value * 1.5;
}
更简洁的写法(C++14起):
template<typename T>
std::enable_if_t<std::is_integral_v<T>, T>
multiply_by_two(T value) {
return value * 2;
}
12.4.3 类型特征检测
检测类型是否有特定特征:
// 检测类型是否有begin()和end()方法
template<typename T>
class is_container {
private:
// 测试函数:尝试调用begin()和end()
template<typename U>
static auto test(int) -> decltype(
std::declval<U>().begin(),
std::declval<U>().end(),
std::true_type{}
);
// 后备函数:如果上面的测试失败,使用这个
template<typename>
static std::false_type test(...);
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
// 使用
static_assert(is_container<std::vector<int>>::value, "vector is container");
static_assert(!is_container<int>::value, "int is not container");
12.5 模板元编程 - 编译时的魔法
12.5.1 编译时计算
模板元编程就像提前计算:在编译时完成计算,运行时直接使用结果。
// 编译时计算阶乘
template<int N>
struct Factorial {
static constexpr int value = N * Factorial<N - 1>::value;
};
template<>
struct Factorial<0> {
static constexpr int value = 1;
};
// 使用:编译时就计算出结果
constexpr int fact5 = Factorial<5>::value; // 编译时就知道是120
更实用的例子:编译时计算数组大小
template<typename T, size_t N>
constexpr size_t array_size(const T(&)[N]) {
return N;
}
int data[] = {1, 2, 3, 4, 5};
constexpr size_t size = array_size(data); // 编译时就知道是5
12.5.2 类型列表操作
类型列表就像编译时的容器:可以存储和操作类型信息。
// 类型列表
template<typename... Types>
struct TypeList {
static constexpr size_t size = sizeof...(Types);
};
// 获取类型列表的第一个类型
template<typename List>
struct Head;
template<typename T, typename... Rest>
struct Head<TypeList<T, Rest...>> {
using type = T;
};
// 获取类型列表的其余类型
template<typename List>
struct Tail;
template<typename T, typename... Rest>
struct Tail<TypeList<T, Rest...>> {
using type = TypeList<Rest...>;
};
// 使用
using MyTypes = TypeList<int, double, std::string>;
using FirstType = typename Head<MyTypes>::type; // int
using RestTypes = typename Tail<MyTypes>::type; // TypeList<double, std::string>
12.5.3 条件类型选择
编译时的if语句:
// 条件选择类型
template<bool Condition, typename TrueType, typename FalseType>
struct Conditional {
using type = TrueType;
};
template<typename TrueType, typename FalseType>
struct Conditional<false, TrueType, FalseType> {
using type = FalseType;
};
// 使用:根据条件选择类型
template<typename T>
using AddConstIfPointer = typename Conditional<
std::is_pointer<T>::value,
const T,
T
>::type;
// 使用
AddConstIfPointer<int> x; // int
template<typename T>
using AddConstIfPointer = typename Conditional<
std::is_pointer<T>::value,
const T,
T
>::type;
// 使用
AddConstIfPointer<int> x; // int
AddConstIfPointer<int*> ptr; // const int*
12.6 现代模板技术
12.6.1 变参模板 - 可变参数的数量
变参模板就像可变参数的函数:可以接受任意数量和类型的参数。
// 递归展开参数包
template<typename T>
void print(T t) {
std::cout << t << std::endl;
}
template<typename T, typename... Args>
void print(T t, Args... args) {
std::cout << t << " ";
print(args...); // 递归调用
}
// C++17折叠表达式:更简洁的写法
template<typename... Args>
void print(Args... args) {
((std::cout << args << " "), ...); // 一元右折叠
std::cout << std::endl;
}
实际应用:类型安全的格式化输出
template<typename... Args>
std::string format(const std::string& fmt, Args... args) {
std::ostringstream oss;
size_t arg_index = 0;
for (char c : fmt) {
if (c == '{}' && arg_index < sizeof...(args)) {
// 这里需要更复杂的实现来按顺序输出参数
// 简化版本,仅作演示
std::vector<std::string> arg_strings = {std::to_string(args)...};
oss << arg_strings[arg_index++];
} else {
oss << c;
}
}
return oss.str();
}
12.6.2 折叠表达式(C++17)
折叠表达式就像参数的批量操作:
// 计算所有参数的和
template<typename... Args>
auto sum(Args... args) {
return (args + ...); // 右折叠:arg1 + (arg2 + (arg3 + ...))
}
// 检查所有参数是否为真
template<typename... Args>
bool all(Args... args) {
return (args && ...); // 右折叠:arg1 && (arg2 && (arg3 && ...))
}
// 找出最大值
template<typename T>
T max_value(T t) { return t; }
template<typename T, typename... Args>
T max_value(T first, Args... rest) {
T max_rest = max_value(rest...);
return (first > max_rest) ? first : max_rest;
}
12.6.3 if constexpr(C++17)
编译时的条件判断:只有满足条件的分支会被编译。
template<typename T>
auto get_value(T t) {
if constexpr (std::is_pointer_v<T>) {
return *t; // 只有T是指针时才会编译这一行
} else {
return t; // 只有T不是指针时才会编译这一行
}
}
template<typename T>
void process(T value) {
if constexpr (std::is_integral_v<T>) {
std::cout << "Processing integer: " << value << std::endl;
} else if constexpr (std::is_floating_point_v<T>) {
std::cout << "Processing float: " << value << std::endl;
} else {
std::cout << "Processing other type" << std::endl;
}
}
12.7 概念(Concepts)- C++20的类型约束
12.7.1 概念的基本用法
概念就像类型的"任职要求":明确规定模板参数必须满足的条件。
// 定义一个简单的概念
template<typename T>
concept Numeric = std::is_arithmetic_v<T>;
// 使用概念约束模板参数
template<Numeric T>
T add(T a, T b) {
return a + b;
}
// 更复杂的概念
template<typename T>
concept Container = requires(T t) {
typename T::value_type; // 必须有value_type类型
typename T::iterator; // 必须有iterator类型
{ t.begin() } -> std::same_as<typename T::iterator>; // begin()必须返回iterator
{ t.end() } -> std::same_as<typename T::iterator>; // end()必须返回iterator
{ t.size() } -> std::convertible_to<std::size_t>; // size()必须返回可转换为size_t的类型
};
12.7.2 概念的组合与重载
概念的组合就像任职要求的组合:
// 组合概念
template<typename T>
concept SignedIntegral = std::integral<T> && std::is_signed_v<T>;
template<typename T>
concept UnsignedIntegral = std::integral<T> && !SignedIntegral<T>;
// 基于概念的重载
template<typename T>
void process(T value) {
std::cout << "Generic version\n";
}
template<std::integral T>
void process(T value) {
std::cout << "Integral version: " << value << std::endl;
}
template<std::floating_point T>
void process(T value) {
std::cout << "Floating point version: " << value << std::endl;
}
12.7.3 requires表达式
requires表达式就像类型的能力测试:
template<typename T>
concept Addable = requires(T a, T b) {
{ a + b } -> std::convertible_to<T>; // a + b必须有效且结果能转换为T
};
template<typename T>
concept Streamable = requires(std::ostream& os, T t) {
{ os << t } -> std::same_as<std::ostream&>; // 必须能输出到ostream
};
template<Addable T>
T add(T a, T b) {
return a + b;
}
template<Streamable T>
void print(const T& value) {
std::cout << value << std::endl;
}
12.8 模板优化技巧
12.8.1 编译时优化
利用编译时信息优化运行时性能:
// 编译时计算幂次
template<int N>
struct Power {
template<typename T>
static T calculate(T base) {
if constexpr (N == 0) return T(1);
else if constexpr (N == 1) return base;
else if constexpr (N % 2 == 0) {
T half = Power<N/2>::calculate(base);
return half * half;
} else {
return base * Power<N-1>::calculate(base);
}
}
};
// 使用:编译时确定计算策略
constexpr double result = Power<10>::calculate(2.0); // 高效的幂次计算
12.8.2 代码膨胀控制
避免为每种类型都生成完全独立的代码:
// 不好的做法:完全独立的实例化
template<typename T>
class DataProcessor {
std::vector<T> data;
public:
void process() {
// 大量复杂的处理逻辑
for (auto& item : data) {
// 复杂的算法...
}
}
};
// 好的做法:将通用逻辑提取出来
class DataProcessorBase {
protected:
void commonProcessLogic(size_t data_size) {
// 与类型无关的通用逻辑
// 大量复杂的处理逻辑...
}
};
template<typename T>
class DataProcessor : private DataProcessorBase {
std::vector<T> data;
public:
void process() {
// 类型相关的部分
for (auto& item : data) {
// 类型特定的处理...
}
// 通用部分
commonProcessLogic(data.size());
}
};
12.8.3 类型擦除技术
类型擦除就像多态的模板版本:
// 简单的类型擦除示例
class AnyPrinter {
struct Concept {
virtual ~Concept() = default;
virtual void print() const = 0;
};
template<typename T>
struct Model : Concept {
T value;
explicit Model(T v) : value(std::move(v)) {}
void print() const override {
std::cout << value << std::endl;
}
};
std::unique_ptr<Concept> object;
public:
template<typename T>
AnyPrinter(T value) : object(std::make_unique<Model<T>>(std::move(value))) {}
void print() const {
if (object) object->print();
}
};
// 使用:只需要一个AnyPrinter类,可以存储任何可输出的类型
AnyPrinter p1(42);
AnyPrinter p2(std::string("hello"));
AnyPrinter p3(3.14);
p1.print(); // 42
p2.print(); // hello
p3.print(); // 3.14
12.9 综合实战项目
12.9.1 通用容器包装器
#include <iostream>
#include <vector>
#include <list>
#include <deque>
#include <algorithm>
#include <memory>
#include <concepts>
// 概念定义
template<typename T>
concept Container = requires(T t) {
typename T::value_type;
typename T::iterator;
{ t.begin() } -> std::same_as<typename T::iterator>;
{ t.end() } -> std::same_as<typename T::iterator>;
{ t.size() } -> std::convertible_to<std::size_t>;
};
// 通用的容器包装器
template<Container ContainerType>
class ContainerWrapper {
ContainerType container;
public:
using value_type = typename ContainerType::value_type;
// 构造函数
ContainerWrapper() = default;
explicit ContainerWrapper(const ContainerType& cont) : container(cont) {}
explicit ContainerWrapper(ContainerType&& cont) : container(std::move(cont)) {}
// 添加元素
template<typename... Args>
void emplace_back(Args&&... args) {
if constexpr (requires { container.emplace_back(std::forward<Args>(args)...); }) {
container.emplace_back(std::forward<Args>(args)...);
} else {
container.insert(container.end(), value_type(std::forward<Args>(args)...));
}
}
// 访问元素
value_type& operator[](size_t index) {
if constexpr (requires { container[index]; }) {
return container[index];
} else {
auto it = container.begin();
std::advance(it, index);
return *it;
}
}
const value_type& operator[](size_t index) const {
if constexpr (requires { container[index]; }) {
return container[index];
} else {
auto it = container.begin();
std::advance(it, index);
return *it;
}
}
// 获取大小
size_t size() const {
return container.size();
}
// 判断是否为空
bool empty() const {
return container.empty();
}
// 排序
template<typename Compare = std::less<value_type>>
void sort(Compare comp = Compare{}) {
if constexpr (requires { std::sort(container.begin(), container.end(), comp); }) {
std::sort(container.begin(), container.end(), comp);
} else {
// 对于不支持随机访问的容器,使用list的排序方法
container.sort(comp);
}
}
// 查找元素
template<typename T>
auto find(const T& value) {
return std::find(container.begin(), container.end(), value);
}
// 打印内容
void print(const std::string& name = "Container") const {
std::cout << name << " contents: ";
for (const auto& item : container) {
std::cout << item << " ";
}
std::cout << std::endl;
}
// 获取底层容器(高级用法)
ContainerType& get_container() { return container; }
const ContainerType& get_container() const { return container; }
};
// 工厂函数
template<Container ContainerType>
auto make_wrapper(ContainerType&& container) {
return ContainerWrapper<std::decay_t<ContainerType>>(std::forward<ContainerType>(container));
}
int main() {
std::cout << "=== 通用容器包装器演示 ===\n\n";
// 测试不同的容器类型
std::vector<int> vec{3, 1, 4, 1, 5, 9, 2, 6};
std::list<int> lst{3, 1, 4, 1, 5, 9, 2, 6};
std::deque<int> deq{3, 1, 4, 1, 5, 9, 2, 6};
auto vec_wrapper = make_wrapper(vec);
auto lst_wrapper = make_wrapper(lst);
auto deq_wrapper = make_wrapper(deq);
std::cout << "原始数据:\n";
vec_wrapper.print("Vector");
lst_wrapper.print("List");
deq_wrapper.print("Deque");
std::cout << "\n元素访问测试:\n";
std::cout << "vec_wrapper[3] = " << vec_wrapper[3] << std::endl;
std::cout << "lst_wrapper[3] = " << lst_wrapper[3] << std::endl;
std::cout << "deq_wrapper[3] = " << deq_wrapper[3] << std::endl;
std::cout << "\n排序测试:\n";
vec_wrapper.sort();
lst_wrapper.sort();
deq_wrapper.sort();
vec_wrapper.print("Sorted Vector");
lst_wrapper.print("Sorted List");
deq_wrapper.print("Sorted Deque");
std::cout << "\n添加元素测试:\n";
vec_wrapper.emplace_back(42);
lst_wrapper.emplace_back(42);
deq_wrapper.emplace_back(42);
vec_wrapper.print("Vector after adding 42");
lst_wrapper.print("List after adding 42");
deq_wrapper.print("Deque after adding 42");
std::cout << "\n查找测试:\n";
auto it1 = vec_wrapper.find(5);
auto it2 = lst_wrapper.find(99);
std::cout << "Found 5 in vector: " << (it1 != vec_wrapper.get_container().end()) << std::endl;
std::cout << "Found 99 in list: " << (it2 != lst_wrapper.get_container().end()) << std::endl;
return 0;
}
12.9.2 类型安全的配置系统
#include <iostream>
#include <string>
#include <unordered_map>
#include <any>
#include <concepts>
#include <typeinfo>
#include <memory>
// 可转换的概念
template<typename From, typename To>
concept ConvertibleTo = std::convertible_to<From, To>;
// 配置项的概念
template<typename T>
concept ConfigValue = std::copy_constructible<T> &&
std::default_initializable<T> &&
requires(std::ostream& os, const T& t) {
{ os << t } -> std::same_as<std::ostream&>;
};
// 类型安全的配置类
template<ConfigValue... SupportedTypes>
class TypeSafeConfig {
std::unordered_map<std::string, std::any> config;
// 检查类型是否在支持的类型列表中
template<typename T>
static constexpr bool is_supported() {
return (std::is_same_v<T, SupportedTypes> || ...);
}
public:
// 设置配置项
template<typename T>
void set(const std::string& key, const T& value) {
static_assert(is_supported<T>(), "Type not supported by this config");
config[key] = value;
}
// 获取配置项
template<typename T>
T get(const std::string& key) const {
static_assert(is_supported<T>(), "Type not supported by this config");
auto it = config.find(key);
if (it == config.end()) {
throw std::runtime_error("Config key not found: " + key);
}
try {
return std::any_cast<T>(it->second);
} catch (const std::bad_any_cast& e) {
throw std::runtime_error("Type mismatch for key: " + key);
}
}
// 安全的获取,带默认值
template<typename T>
T get(const std::string& key, const T& default_value) const {
static_assert(is_supported<T>(), "Type not supported by this config");
try {
return get<T>(key);
} catch (const std::runtime_error&) {
return default_value;
}
}
// 检查配置项是否存在
bool has(const std::string& key) const {
return config.find(key) != config.end();
}
// 删除配置项
void remove(const std::string& key) {
config.erase(key);
}
// 清空所有配置
void clear() {
config.clear();
}
// 获取配置项的数量
size_t size() const {
return config.size();
}
// 打印所有配置(用于调试)
void print() const {
std::cout << "Configuration contents:\n";
for (const auto& [key, value] : config) {
std::cout << " " << key << ": ";
// 尝试按每种支持的类型打印
bool printed = false;
((printed = printed || print_if_type<SupportedTypes>(key, value)), ...);
if (!printed) {
std::cout << "<unknown type>";
}
std::cout << std::endl;
}
}
private:
// 辅助函数:如果类型匹配就打印
template<typename T>
bool print_if_type(const std::string& key, const std::any& value) const {
try {
auto val = std::any_cast<T>(value);
std::cout << val;
return true;
} catch (const std::bad_any_cast&) {
return false;
}
}
};
// 创建常用配置类型的别名
using BasicConfig = TypeSafeConfig<int, double, std::string, bool>;
using NumericConfig = TypeSafeConfig<int, float, double, long, long long>;
int main() {
std::cout << "=== 类型安全配置系统演示 ===\n\n";
// 创建基本配置
BasicConfig config;
std::cout << "1. 设置配置项:\n";
config.set("port", 8080);
config.set("timeout", 30.5);
config.set("hostname", std::string("localhost"));
config.set("debug_mode", true);
config.set("max_connections", 100);
config.print();
std::cout << "配置项数量: " << config.size() << std::endl;
std::cout << "\n2. 获取配置项:\n";
std::cout << "Port: " << config.get<int>("port") << std::endl;
std::cout << "Timeout: " << config.get<double>("timeout") << std::endl;
std::cout << "Hostname: " << config.get<std::string>("hostname") << std::endl;
std::cout << "Debug mode: " << std::boolalpha << config.get<bool>("debug_mode") << std::endl;
std::cout << "\n3. 安全获取(带默认值):\n";
std::cout << "Max connections: " << config.get<int>("max_connections", 50) << std::endl;
std::cout << "Non-existent key: " << config.get<std::string>("non_existent", "default_value") << std::endl;
std::cout << "\n4. 类型安全检查:\n";
try {
// 这会编译失败,因为char不在SupportedTypes列表中
// config.set("test", 'c');
// 这会在运行时失败,因为类型不匹配
int port = config.get<int>("hostname");
} catch (const std::runtime_error& e) {
std::cout << "Caught expected error: " << e.what() << std::endl;
}
std::cout << "\n5. 修改配置:\n";
config.set("port", 9090); // 修改现有配置
config.remove("debug_mode"); // 删除配置
std::cout << "After modifications:\n";
config.print();
std::cout << "配置项数量: " << config.size() << std::endl;
std::cout << "\n6. 检查配置存在:\n";
std::cout << "Has port: " << config.has("port") << std::endl;
std::cout << "Has debug_mode: " << config.has("debug_mode") << std::endl;
std::cout << "\n7. 使用不同的配置类型:\n";
NumericConfig num_config;
num_config.set("pi", 3.14159265359);
num_config.set("e", 2.71828182846);
num_config.set("answer", 42);
num_config.set("large_number", 1234567890LL);
num_config.print();
return 0;
}
12.10 学习要点总结
12.10.1 核心概念回顾
- 模板基础:理解模板参数、实例化、特化的概念
- SFINAE:掌握替换失败不是错误的原理和应用
- 模板元编程:学会在编译时进行计算和类型操作
- 现代特性:掌握变参模板、折叠表达式、if constexpr
- 概念约束:理解C++20的概念机制,写出更清晰的模板代码
12.10.2 最佳实践
- 明确约束:使用概念或SFINAE明确表达模板参数的要求
- 错误友好:提供有用的错误信息,帮助用户正确使用模板
- 性能考虑:注意编译时间和代码大小,避免过度复杂的模板元编程
- 代码复用:合理使用特化和代码因式分解,避免代码膨胀
- 现代语法:优先使用if constexpr和概念,而不是复杂的SFINAE
12.10.3 常见陷阱
- 过度复杂:不要为了炫技而使用过于复杂的模板技巧
- 编译错误:模板错误信息通常很难读,要仔细设计模板接口
- 代码膨胀:注意每种模板实例化都会生成独立的代码
- 编译时间:复杂的模板元编程可能显著增加编译时间
- 可维护性:确保你的模板代码对团队其他成员来说是可理解的
通过掌握模板编程,你可以写出更通用、更高效、更类型安全的C++代码。记住:模板是工具,不是目的 - 用它们来解决实际问题,而不是制造复杂性!
更多推荐



所有评论(0)