强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

C/C++ Linux 开发教程(GCC + CMake) / 模板与泛型编程

模板与泛型编程

1. 函数模板

函数模板让编译器根据调用时的参数类型自动推导并生成对应版本的函数。

#include <iostream>
#include <string>

// 基本函数模板
template <typename T>
T max_value(T a, T b) {
    return (a > b) ? a : b;
}

// 多类型参数
template <typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
    return a + b;
}

// 显式指定模板参数
template <typename T>
void print_type(T value) {
    std::cout << "值: " << value
              << ", 类型大小: " << sizeof(T) << " 字节\n";
}

int main() {
    // 自动推导
    std::cout << max_value(3, 5) << "\n";        // int
    std::cout << max_value(3.14, 2.72) << "\n";  // double
    std::cout << max_value<std::string>("hello", "world") << "\n"; // 显式指定

    // 多类型参数
    std::cout << add(3, 4.5) << "\n";    // double (7.5)
    std::cout << add(10, 20) << "\n";    // int (30)

    // 显式指定
    print_type<int>(42);
    print_type<double>(3.14);

    return 0;
}

编译运行:

g++ -std=c++17 -o demo demo.cpp && ./demo

2. 类模板

#include <iostream>
#include <stdexcept>

template <typename T, size_t N>
class FixedArray {
    T data_[N];
    size_t size_ = 0;

public:
    void push_back(const T& value) {
        if (size_ >= N) {
            throw std::overflow_error("FixedArray 已满");
        }
        data_[size_++] = value;
    }

    T& operator[](size_t index) { return data_[index]; }
    const T& operator[](size_t index) const { return data_[index]; }

    size_t size() const { return size_; }
    size_t capacity() const { return N; }

    // 类模板内部使用类名时可省略模板参数
    class Iterator {
        T* ptr_;
    public:
        explicit Iterator(T* p) : ptr_(p) {}
        T& operator*() { return *ptr_; }
        Iterator& operator++() { ++ptr_; return *this; }
        bool operator!=(const Iterator& other) const { return ptr_ != other.ptr_; }
    };

    Iterator begin() { return Iterator(data_); }
    Iterator end() { return Iterator(data_ + size_); }
};

int main() {
    FixedArray<int, 5> arr;
    arr.push_back(10);
    arr.push_back(20);
    arr.push_back(30);

    for (const auto& v : arr) {
        std::cout << v << " ";
    }
    std::cout << "\n";

    FixedArray<std::string, 3> names;
    names.push_back("Alice");
    names.push_back("Bob");

    for (const auto& name : names) {
        std::cout << name << " ";
    }
    std::cout << "\n";

    return 0;
}

3. 模板特化

全特化

#include <iostream>
#include <cstring>

// 通用版本
template <typename T>
class Comparator {
public:
    static bool equal(T a, T b) {
        std::cout << "通用比较\n";
        return a == b;
    }
};

// 全特化:const char* 版本
template <>
class Comparator<const char*> {
public:
    static bool equal(const char* a, const char* b) {
        std::cout << "字符串比较\n";
        return std::strcmp(a, b) == 0;
    }
};

// 全特化:double 版本(考虑精度)
template <>
class Comparator<double> {
public:
    static bool equal(double a, double b) {
        std::cout << "浮点数比较 (eps)\n";
        constexpr double eps = 1e-9;
        return (a - b > -eps) && (a - b < eps);
    }
};

int main() {
    std::cout << std::boolalpha;
    std::cout << Comparator<int>::equal(5, 5) << "\n";           // true
    std::cout << Comparator<const char*>::equal("hi", "hi") << "\n"; // true
    std::cout << Comparator<double>::equal(0.1 + 0.2, 0.3) << "\n";  // true

    return 0;
}

偏特化

#include <iostream>
#include <vector>

// 通用版本
template <typename T, typename U>
struct IsSame {
    static constexpr bool value = false;
};

// 偏特化:两个类型相同时
template <typename T>
struct IsSame<T, T> {
    static constexpr bool value = true;
};

// 指针类型偏特化
template <typename T>
struct RemovePointer {
    using type = T;
};

template <typename T>
struct RemovePointer<T*> {
    using type = T;
};

int main() {
    std::cout << std::boolalpha;
    std::cout << IsSame<int, int>::value << "\n";      // true
    std::cout << IsSame<int, double>::value << "\n";    // false

    std::cout << IsSame<RemovePointer<int*>::type, int>::value << "\n";   // true
    std::cout << IsSame<RemovePointer<int>::type, int>::value << "\n";    // true

    return 0;
}

4. 模板参数类型

参数类型示例说明
类型参数template<typename T>最常用
非类型参数template<int N>编译期常量值
模板模板参数template<template<class> class C>模板作为参数
#include <iostream>
#include <vector>
#include <list>

// 非类型参数
template <typename T, size_t N>
struct Array {
    T data[N];
    constexpr size_t size() const { return N; }
};

// C++17:auto 非类型参数
template <auto Value>
void print_value() {
    std::cout << "值: " << Value << ", 类型大小: " << sizeof(Value) << "\n";
}

// 模板模板参数
template <typename T, template <typename, typename> class Container>
class Stack {
    Container<T, std::allocator<T>> data_;

public:
    void push(const T& value) { data_.push_back(value); }
    void pop() { data_.pop_back(); }
    const T& top() const { return data_.back(); }
    bool empty() const { return data_.empty(); }
};

int main() {
    // 非类型参数
    Array<int, 10> arr;
    std::cout << "大小: " << arr.size() << "\n";

    // auto 非类型参数
    print_value<42>();         // int
    print_value<3.14>();       // double
    print_value<'A'>();        // char

    // 模板模板参数
    Stack<int, std::vector> vecStack;
    vecStack.push(1);
    vecStack.push(2);
    std::cout << "vector 栈顶: " << vecStack.top() << "\n";  // 2

    Stack<int, std::list> listStack;
    listStack.push(10);
    listStack.push(20);
    std::cout << "list 栈顶: " << listStack.top() << "\n";   // 20

    return 0;
}

5. SFINAE 概念

SFINAE(Substitution Failure Is Not An Error):模板参数替换失败不是错误,编译器会尝试其他重载。

#include <iostream>
#include <type_traits>

// C++17 之前的手动 SFINAE
template <typename T>
typename std::enable_if<std::is_integral<T>::value, void>::type
print_info(T value) {
    std::cout << value << " 是整数\n";
}

template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, void>::type
print_info(T value) {
    std::cout << value << " 是浮点数\n";
}

// 检测是否有 size() 成员函数
template <typename T, typename = void>
struct has_size : std::false_type {};

template <typename T>
struct has_size<T, std::void_t<decltype(std::declval<T>().size())>> : std::true_type {};

int main() {
    print_info(42);      // 整数
    print_info(3.14);    // 浮点数

    std::cout << std::boolalpha;
    std::cout << "vector 有 size(): " << has_size<std::vector<int>>::value << "\n";  // true
    std::cout << "int 有 size(): " << has_size<int>::value << "\n";                   // false

    return 0;
}

💡 提示:C++20 Concepts 大幅简化了 SFINAE 的写法,推荐新项目使用 Concepts。


6. 可变参数模板(Variadic Templates)

#include <iostream>
#include <string>

// 递归终止条件
void print() {
    std::cout << "\n";
}

// 可变参数模板函数
template <typename T, typename... Args>
void print(const T& first, const Args&... rest) {
    std::cout << first;
    if constexpr (sizeof...(rest) > 0) {
        std::cout << ", ";
    }
    print(rest...);
}

// 使用折叠表达式 (C++17) 的更简洁版本
template <typename... Args>
void print_fold(const Args&... args) {
    ((std::cout << args << " "), ...);  // 逗号折叠
    std::cout << "\n";
}

// 可变参数求和
template <typename... Args>
auto sum(const Args&... args) {
    return (args + ...);  // 加法折叠
}

int main() {
    print(1, "hello", 3.14, 'A');       // 1, hello, 3.14, A
    print_fold(1, "hello", 3.14, 'A');  // 1 hello 3.14 A

    std::cout << "sum = " << sum(1, 2, 3, 4, 5) << "\n";  // 15

    return 0;
}

7. 折叠表达式(Fold Expressions, C++17)

折叠形式展开结果说明
(pack op ...)(E1 op (E2 op (... op En)))一元右折叠
(... op pack)((E1 op E2) op ...) op En)一元左折叠
(pack op ... op init)(E1 op (E2 op (... op (En op init))))二元右折叠
(init op ... op pack)(((init op E1) op E2) op ...) op En)二元左折叠
#include <iostream>
#include <string>
#include <vector>

// 所有参数都为 true
template <typename... Args>
bool all_true(Args... args) {
    return (args && ...);  // 逻辑与折叠
}

// 所有参数都满足条件
template <typename Pred, typename... Args>
bool all_of_pred(Pred pred, Args... args) {
    return (pred(args) && ...);
}

// 向 vector 追加多个元素
template <typename T, typename... Args>
void push_all(std::vector<T>& vec, Args&&... args) {
    (vec.push_back(std::forward<Args>(args)), ...);  // 逗号折叠
}

// 打印所有参数(带索引)
template <typename... Args>
void print_indexed(const Args&... args) {
    size_t index = 0;
    ((std::cout << "[" << index++ << "] " << args << "\n"), ...);
}

int main() {
    std::cout << std::boolalpha;
    std::cout << all_true(true, true, true) << "\n";   // true
    std::cout << all_true(true, false, true) << "\n";   // false

    auto is_positive = [](int x) { return x > 0; };
    std::cout << all_of_pred(is_positive, 1, 2, 3) << "\n";  // true
    std::cout << all_of_pred(is_positive, 1, -1, 3) << "\n"; // false

    std::vector<int> v;
    push_all(v, 10, 20, 30, 40);
    for (auto x : v) std::cout << x << " ";
    std::cout << "\n";

    print_indexed("hello", 42, 3.14);

    return 0;
}

8. 模板元编程基础

#include <iostream>
#include <type_traits>

// 编译期阶乘
template <int N>
struct Factorial {
    static constexpr int64_t value = N * Factorial<N - 1>::value;
};

template <>
struct Factorial<0> {
    static constexpr int64_t value = 1;
};

// 编译期斐波那契
template <int N>
struct Fibonacci {
    static constexpr int64_t value = Fibonacci<N - 1>::value + Fibonacci<N - 2>::value;
};

template <>
struct Fibonacci<0> { static constexpr int64_t value = 0; };
template <>
struct Fibonacci<1> { static constexpr int64_t value = 1; };

// 类型列表操作
template <typename... Ts>
struct TypeList {};

template <typename List>
struct Size;

template <typename... Ts>
struct Size<TypeList<Ts...>> {
    static constexpr size_t value = sizeof...(Ts);
};

// 获取第一个类型
template <typename List>
struct Front;

template <typename Head, typename... Tail>
struct Front<TypeList<Head, Tail...>> {
    using type = Head;
};

int main() {
    // 编译期计算
    static_assert(Factorial<10>::value == 3628800);
    static_assert(Fibonacci<10>::value == 55);

    std::cout << "10! = " << Factorial<10>::value << "\n";
    std::cout << "fib(10) = " << Fibonacci<10>::value << "\n";

    // 类型列表
    using MyTypes = TypeList<int, double, char, float>;
    static_assert(Size<MyTypes>::value == 4);
    static_assert(std::is_same_v<Front<MyTypes>::type, int>);

    return 0;
}

9. constexpr if (C++17)

constexpr if 在编译期根据条件选择代码路径,替代 SFINAE。

#include <iostream>
#include <type_traits>
#include <string>

// 使用 constexpr if 实现类型分发
template <typename T>
std::string describe(T value) {
    if constexpr (std::is_integral_v<T>) {
        return "整数: " + std::to_string(value);
    } else if constexpr (std::is_floating_point_v<T>) {
        return "浮点数: " + std::to_string(value);
    } else if constexpr (std::is_pointer_v<T>) {
        return "指针";
    } else {
        return "其他类型";
    }
}

// 递归的可变参数打印(无须终止函数)
template <typename First, typename... Rest>
void print_all(const First& first, const Rest&... rest) {
    std::cout << first;
    if constexpr (sizeof...(rest) > 0) {
        std::cout << ", ";
        print_all(rest...);
    } else {
        std::cout << "\n";
    }
}

// 编译期类型检测
template <typename T>
auto to_string_if_possible(T&& value) {
    if constexpr (std::is_arithmetic_v<std::decay_t<T>>) {
        return std::to_string(std::forward<T>(value));
    } else {
        return std::forward<T>(value);
    }
}

int main() {
    std::cout << describe(42) << "\n";        // 整数
    std::cout << describe(3.14) << "\n";      // 浮点数
    std::cout << describe(&describe<int>) << "\n"; // 指针

    print_all(1, "hello", 3.14, 'A');

    std::cout << to_string_if_possible(42) << "\n";
    std::cout << to_string_if_possible(3.14) << "\n";

    return 0;
}

10. Concepts (C++20) 简介

#include <iostream>
#include <concepts>
#include <vector>
#include <string>

// 定义 Concept
template <typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;

template <typename T>
concept HasSize = requires(T t) {
    { t.size() } -> std::convertible_to<size_t>;
};

// 使用 Concept 约束函数模板
template <Numeric T>
T safe_add(T a, T b) {
    return a + b;
}

// 简写语法 (C++20)
auto safe_multiply(Numeric auto a, Numeric auto b) {
    return a * b;
}

// requires 子句
template <typename T>
    requires HasSize<T>
void print_size(const T& container) {
    std::cout << "大小: " << container.size() << "\n";
}

int main() {
    std::cout << safe_add(3, 4) << "\n";       // 7
    std::cout << safe_add(1.5, 2.5) << "\n";   // 4.0
    std::cout << safe_multiply(3, 4.5) << "\n"; // 13.5

    std::vector<int> v = {1, 2, 3};
    std::string s = "hello";
    print_size(v);   // 大小: 3
    print_size(s);   // 大小: 5

    // print_size(42);  // 编译错误:int 没有 size()

    return 0;
}

编译需要 GCC 10+ 或 Clang 16+:

g++ -std=c++20 -o demo demo.cpp && ./demo

实际场景:泛型工厂模式

#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <functional>

// 泛型工厂
template <typename Base, typename... Args>
class Factory {
    using Creator = std::function<std::unique_ptr<Base>(Args...)>;

    std::unordered_map<std::string, Creator> registry_;

public:
    // 注册产品创建函数
    template <typename Derived>
    void registerType(const std::string& name) {
        registry_[name] = [](Args... args) -> std::unique_ptr<Base> {
            return std::make_unique<Derived>(std::forward<Args>(args)...);
        };
    }

    // 创建产品
    std::unique_ptr<Base> create(const std::string& name, Args... args) {
        auto it = registry_.find(name);
        if (it != registry_.end()) {
            return it->second(std::forward<Args>(args)...);
        }
        return nullptr;
    }
};

// 产品接口
class Shape {
public:
    virtual ~Shape() = default;
    virtual void draw() const = 0;
    virtual double area() const = 0;
};

// 具体产品
class Circle : public Shape {
    double r_;
public:
    explicit Circle(double r) : r_(r) {}
    void draw() const override { std::cout << "Circle(r=" << r_ << ")\n"; }
    double area() const override { return 3.14159 * r_ * r_; }
};

class Square : public Shape {
    double s_;
public:
    explicit Square(double s) : s_(s) {}
    void draw() const override { std::cout << "Square(s=" << s_ << ")\n"; }
    double area() const override { return s_ * s_; }
};

int main() {
    Factory<Shape, double> factory;
    factory.registerType<Circle>("circle");
    factory.registerType<Square>("square");

    auto c = factory.create("circle", 5.0);
    auto s = factory.create("square", 3.0);

    c->draw();  // Circle(r=5)
    s->draw();  // Square(s=3)

    std::cout << "圆面积: " << c->area() << "\n";
    std::cout << "正方形面积: " << s->area() << "\n";

    return 0;
}

扩展阅读