C++ Primer 第16章:模板与泛型编程

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 版本                   │
└─────────────────────────────────────────────────────┘
cpp 复制代码
// 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 类模板

cpp 复制代码
// 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 模板参数

cpp 复制代码
// 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 模板实参推断

cpp 复制代码
// 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 重载与模板

cpp 复制代码
// 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 可变参数模板

cpp 复制代码
// 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 函数模板特化

cpp 复制代码
// 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 类模板特化

cpp 复制代码
// 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 模板与继承

cpp 复制代码
// 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 = &sc;
    p->showAll();

    // CRTP
    ConcreteA a;
    ConcreteB b;
    a.interface();
    b.interface();
    ConcreteA::staticInterface();

    return 0;
}

16.7 综合示例:通用算法库

cpp 复制代码
// 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... Argssizeof...(args) 获取数量,递归展开
折叠表达式 C++17,(args + ...) 一元折叠,(init + ... + args) 二元折叠
函数模板特化 template <> 为特定类型提供专门实现
类模板完全特化 template <> class Foo<int> 针对特定类型
类模板部分特化 template <typename T> class Foo<T*> 针对类型模式
SFINAE enable_if 根据类型特征启用/禁用模板
CRTP 奇异递归模板模式,编译时多态,避免虚函数开销
相关推荐
这个DBA有点耶1 小时前
死锁排查进阶:从日志到根因的完整分析链
java·开发语言·数据库·sql·运维开发·学习方法·dba
三无推导1 小时前
无需扩展的 PHP 加密方案有哪些优势:基于 php.x5.chat 的实践分析
开发语言·php·web开发·数据加密·php加密·php安全·无需扩展
jingling5551 小时前
Flutter | 商城项目鸿蒙(OpenHarmony)适配实战
android·开发语言·前端·flutter·华为·harmonyos
Luminous.1 小时前
C语言--day25
c语言·开发语言
QT-Neal1 小时前
C++智能指针使用详解
开发语言·c++
JAVA9651 小时前
JAVA面试-并发篇 06-ReentrantLock如何实现公平锁的以及可重入吗
java·开发语言·面试
二等饼干~za8986682 小时前
geo优化系统源码搭建保姆式搭建教程
java·开发语言·django·php·音视频
SilentSamsara2 小时前
消息队列集成:Python + Kafka/RabbitMQ 生产实践
服务器·开发语言·分布式·python·kafka·rabbitmq
luj_17682 小时前
硝酸核关联假说缺乏实验证据
c语言·开发语言·c++·经验分享·算法