【C++ Primer】第十六章:模板与泛型编程

模板与泛型编程是 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];
    }
};

最佳实践总结

  1. 使用 typename 和 class 声明类型参数(两者等价)
  2. 模板定义通常放在头文件中
  3. 使用 SFINAE 或概念进行约束
  4. 优先使用可变参数模板而不是函数重载
  5. 理解模板实例化的时机和成本
  6. 使用特化提供特定类型的优化实现

调试技巧

// 在模板中添加静态断言进行编译时检查
template<typename T>
void process(const T& value) {
    static_assert(std::is_arithmetic_v<T>, 
                  "T must be an arithmetic type");
    // ...
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值