第12章:模板编程 - 类型无关的代码魔法

12.1 模板编程基础概念

12.1.1 模板的设计哲学

想象你是一家制造工厂,需要生产不同型号的螺丝。你不会为每种型号都建一条生产线,而是设计一条可调节的生产线,通过更换模具来生产不同型号的产品。C++模板就是程序员的"可调节生产线"。

核心思想

  1. 类型参数化:把类型当作参数传递,就像把模具规格当作参数
  2. 编译时实例化:在编译时根据具体类型生成代码,就像在生产前根据规格调整机器
  3. 零开销抽象:不会增加运行时开销,就像调整机器不会增加材料成本
  4. 类型安全:保持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 两阶段编译检查

模板编译就像定制家具

  1. 设计图纸检查:检查模板本身的语法是否正确(就像检查设计图是否合理)
  2. 实际制作检查:在根据具体尺寸制作时,检查是否可行(就像检查木材是否足够结实)
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 避免代码膨胀

代码膨胀问题:每种模板实例化都会生成独立的机器码,就像为每种产品都建一条完整的生产线。

优化策略

  1. 显式实例化控制:集中管理实例化位置
// 只在源文件中实例化常用版本
// Stack.cpp
template class Stack<int>;
template class Stack<double>;
template class Stack<std::string>;
  1. 代码因式分解:把通用部分提取出来
// 通用基类:不依赖模板参数
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 核心概念回顾

  1. 模板基础:理解模板参数、实例化、特化的概念
  2. SFINAE:掌握替换失败不是错误的原理和应用
  3. 模板元编程:学会在编译时进行计算和类型操作
  4. 现代特性:掌握变参模板、折叠表达式、if constexpr
  5. 概念约束:理解C++20的概念机制,写出更清晰的模板代码

12.10.2 最佳实践

  1. 明确约束:使用概念或SFINAE明确表达模板参数的要求
  2. 错误友好:提供有用的错误信息,帮助用户正确使用模板
  3. 性能考虑:注意编译时间和代码大小,避免过度复杂的模板元编程
  4. 代码复用:合理使用特化和代码因式分解,避免代码膨胀
  5. 现代语法:优先使用if constexpr和概念,而不是复杂的SFINAE

12.10.3 常见陷阱

  1. 过度复杂:不要为了炫技而使用过于复杂的模板技巧
  2. 编译错误:模板错误信息通常很难读,要仔细设计模板接口
  3. 代码膨胀:注意每种模板实例化都会生成独立的代码
  4. 编译时间:复杂的模板元编程可能显著增加编译时间
  5. 可维护性:确保你的模板代码对团队其他成员来说是可理解的

通过掌握模板编程,你可以写出更通用、更高效、更类型安全的C++代码。记住:模板是工具,不是目的 - 用它们来解决实际问题,而不是制造复杂性!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐