乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • C++ 学习路径
  • 第1章 C++概述与开发环境

    • C++ 语言概述与编译模型
    • 第一个 C++ 程序与规范
    • 开发环境配置
    • 预处理指令详解
  • 第2章 基本语法与类型系统

    • 变量与基本类型
    • 类型转换
    • 枚举类型
    • 引用与指针
    • 数组与 stdarray
    • 字符串与原始字符串字面量
    • const 与 constexpr
    • nullptr 与空指针
    • 类型推导 auto 与 decltype
    • 基于范围的 for 循环
    • 值类别全面解析
    • char16_t 与 char32_t
    • static_assert 编译期断言
  • 第3章 函数与重载

    • 函数声明与定义
    • 函数重载
    • 默认参数与内联函数
    • Lambda 表达式
    • 函数对象与 stdfunction
    • 后置返回类型与 noexcept
  • 第4章 类与对象

    • 类的基本定义
    • 构造函数与析构函数
    • 拷贝控制
    • 移动构造函数与移动赋值
    • 列表初始化与类内初始化器
    • 静态成员与嵌套类
    • 友元
    • =default 与 =delete
  • 第5章 继承与多态

    • 继承基础
    • 虚函数与多态
    • 虚函数表与动态绑定原理
    • 虚析构函数
    • 抽象类与纯虚函数
    • 多重继承与虚继承
    • 继承构造函数
  • 第6章 运算符重载

    • 运算符重载基础
    • 算术与关系运算符重载
    • 赋值与移动运算符重载
    • 特殊运算符重载
  • 第7章 模板与泛型编程

    • 函数模板
    • 类模板
    • 模板特化与偏特化
    • 可变参数模板
    • 别名模板与模板模板参数
    • SFINAE 与类型萃取
    • 依赖名与 typename/template 关键字
  • 第8章 异常处理

    • 异常处理机制
    • noexcept 与异常安全
    • RAII 与异常安全实践
  • 第9章 内存管理与智能指针

    • 动态内存与内存分区
    • RAII 惯用法
    • unique_ptr
    • shared_ptr 与 weak_ptr
    • 内存管理最佳实践
  • 第10章 右值引用与移动语义

    • 右值引用与值类别深度解析
    • std::move 原理与使用
    • 完美转发与 std::forward
    • 移动语义性能对比与最佳实践
  • 第11章 STL容器

    • vector 深度剖析
    • deque 内部机制
    • list 与 forward_list
    • map 与 set 深度解析
    • unordered 容器与哈希原理
    • array 与 tuple
    • 容器适配器
    • 容器选择全景指南
  • 第12章 STL算法与迭代器

    • 迭代器体系全解
    • 非变异算法
    • 变异算法
    • 排序与二分算法
    • Lambda 与算法组合
    • std::random 随机数库
    • 自定义迭代器开发
  • 第13章 IO流与文件

    • 标准 IO 流
    • 格式化输出控制
    • 文件流操作
    • 字符串流
    • std::regex 正则表达式
  • 第14章 并发与多线程

    • thread 基础与线程管理
    • mutex 与 lock_guard
    • unique_lock 与 condition_variable
    • thread_local 线程局部存储
    • atomic 与内存序
    • future 与 async 异步编程
    • std::chrono 时间库
  • 第15章 现代C++新特性

    • 从 C++11 到 C++20 演进路线
    • C++14 关键新特性
    • C++17 关键新特性
    • C++20 核心特性速览
  • 第16章 面试考点与最佳实践

    • C++ 综合最佳实践清单
    • 高频面试题精讲
    • 多线程面试题与实战
    • 内存管理常见陷阱与排查
  • 附录

    • C++ 核心知识点
    • C++ 专业术语

别名模板与模板模板参数

定义与作用

别名模板(Alias Template,C++11)为模板类型创建语义化的别名:

template <typename T>
using StringMap = std::map<std::string, T>;
// StringMap<int> 等价于 std::map<std::string, int>

模板模板参数(Template Template Parameter)允许模板接受另一个模板作为参数:

template <template <typename> class Container>
class Wrapper { Container<int> data_; };

别名模板不仅让代码更可读,还是模板元编程中类型转换的重要工具。

核心原理

typedef vs using 别名

模板模板参数的匹配

完整示例

示例一:学生成绩管理系统的容器抽象

场景说明:设计一个学生成绩管理系统,通过别名模板和模板模板参数让底层容器可替换。

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <algorithm>

// ====== 别名模板:简化常用容器声明 ======

template <typename T>
using Vector = std::vector<T>;

// 学生名 → 某类型值的映射
template <typename ValueType>
using ScoreSheet = std::map<std::string, ValueType>;

// 学生名 → 成绩列表
template <typename ScoreType>
using StudentScores = ScoreSheet<Vector<ScoreType>>;

// ====== 模板模板参数:可替换底层容器的包装类 ======

template <typename T, template <typename...> class Container = Vector>
class ScoreManager {
public:
    void addScore(const std::string& student, T score) {
        data_[student].push_back(score);
    }

    double average(const std::string& student) const {
        auto it = data_.find(student);
        if (it == data_.end() || it->second.empty()) return 0.0;

        T sum = T{};
        for (const auto& s : it->second) sum += s;
        return static_cast<double>(sum) / it->second.size();
    }

    T highest(const std::string& student) const {
        auto it = data_.find(student);
        if (it == data_.end() || it->second.empty()) return T{};
        return *std::max_element(it->second.begin(), it->second.end());
    }

    void printReport() const {
        std::cout << "========== 成绩报告 ==========" << std::endl;
        for (const auto& [student, scores] : data_) {
            std::cout << student << ": ";
            std::cout << "[";
            for (size_t i = 0; i < scores.size(); ++i) {
                if (i > 0) std::cout << ", ";
                std::cout << scores[i];
            }
            std::cout << "]";
            std::cout << "  平均: " << average(student)
                      << "  最高: " << highest(student) << std::endl;
        }
    }

private:
    StudentScores<T> data_;  // 使用别名模板
};

// ====== 别名模板做类型萃取 ======

template <typename T>
using RemoveRef = typename std::remove_reference<T>::type;

template <typename T>
using AddConst = const T;

template <typename T>
using Ptr = T*;

int main() {
    std::cout << "===== 别名模板 —— 简化声明 =====" << std::endl;

    // 等价于 std::map<std::string, std::vector<int>>
    StudentScores<int> scores;

    scores["张三"] = Vector<int>{85, 92, 78};
    scores["李四"] = Vector<int>{90, 95, 88, 76};
    scores["王五"] = Vector<int>{70, 80, 75};

    for (const auto& [name, vec] : scores) {
        std::cout << name << " 有 " << vec.size() << " 门成绩" << std::endl;
    }

    std::cout << "\n===== 模板模板参数 —— 可替换容器 =====" << std::endl;

    ScoreManager<int> mgr;
    mgr.addScore("张三", 85);
    mgr.addScore("张三", 92);
    mgr.addScore("张三", 78);
    mgr.addScore("李四", 90);
    mgr.addScore("李四", 95);
    mgr.addScore("李四", 88);
    mgr.printReport();

    // 类型萃取别名
    std::cout << "\n===== 类型萃取别名 =====" << std::endl;
    using RefInt = int&;
    using BareInt = RemoveRef<RefInt>;  // = int
    std::cout << "RemoveRef<int&> is int? "
              << std::is_same<BareInt, int>::value << std::endl;

    Ptr<const int> p = nullptr;  // AddConst<int>* → const int*
    std::cout << "Ptr<AddConst<int>> is const int*? "
              << std::is_same<Ptr<AddConst<int>>, const int*>::value << std::endl;

    return 0;
}

预期输出:

===== 别名模板 —— 简化声明 =====
张三 有 3 门成绩
李四 有 4 门成绩
王五 有 3 门成绩

===== 模板模板参数 —— 可替换容器 =====
========== 成绩报告 ==========
张三: [85, 92, 78]  平均: 85  最高: 92
李四: [90, 95, 88]  平均: 91  最高: 95

===== 类型萃取别名 =====
RemoveRef<int&> is int? true
Ptr<AddConst<int>> is const int*? true

逐段分析:

  • template<typename T> using Vector = std::vector<T> 是别名模板——Vector<int> 即 std::vector<int>
  • template<typename T> using ScoreSheet = std::map<std::string, T> 嵌套别名——减少嵌套 < > 的层级
  • template<typename...> class Container 用参数包匹配任意参数数量的模板——C++17 后推荐的模板模板参数写法
  • 类型萃取别名让模板元编程代码更简洁:RemoveRef<T> 代替 typename std::remove_reference<T>::type

示例二:容器工厂模式

#include <iostream>
#include <vector>
#include <list>
#include <deque>
#include <memory>

// 模板模板参数:通过编译期策略切换底层容器
template <typename T, template <typename...> class Container = std::vector>
class ObjectPool {
public:
    template <typename... Args>
    T* acquire(Args&&... args) {
        auto obj = std::make_unique<T>(std::forward<Args>(args)...);
        T* ptr = obj.get();
        pool_.push_back(std::move(obj));
        return ptr;
    }

    size_t size() const { return pool_.size(); }

    void print() const {
        std::cout << "Pool 中的对象 (" << pool_.size() << "):" << std::endl;
        for (const auto& obj : pool_) {
            std::cout << "  @ " << obj.get() << std::endl;
        }
    }

private:
    // pool_ 是 Container<unique_ptr<T>>
    Container<std::unique_ptr<T>> pool_;
};

struct Widget {
    int id;
    explicit Widget(int id) : id(id) {}
};

int main() {
    std::cout << "===== 使用 vector 作为底层容器 =====" << std::endl;
    ObjectPool<Widget> vector_pool;  // 默认 Container = std::vector
    vector_pool.acquire(1);
    vector_pool.acquire(2);
    vector_pool.acquire(3);
    vector_pool.print();

    std::cout << "\n===== 使用 list 作为底层容器 =====" << std::endl;
    ObjectPool<Widget, std::list> list_pool;
    list_pool.acquire(10);
    list_pool.acquire(20);
    list_pool.print();

    std::cout << "\n===== 使用 deque 作为底层容器 =====" << std::endl;
    ObjectPool<Widget, std::deque> deque_pool;
    deque_pool.acquire(100);
    deque_pool.print();

    return 0;
}

预期输出:

===== 使用 vector 作为底层容器 =====
Pool 中的对象 (3):
  @ 0x...
  @ 0x...
  @ 0x...

===== 使用 list 作为底层容器 =====
Pool 中的对象 (2):
  @ 0x...
  @ 0x...

===== 使用 deque 作为底层容器 =====
Pool 中的对象 (1):
  @ 0x...

易错场景与面试考点

易错场景

场景错误表现正确做法
typedef 试图写模板别名语法不支持用 C++11 using 别名模板
模板模板参数数量不匹配std::vector 有 2 个参数C++17 用 template<typename...> class C 匹配任意
别名模板误导返回类型推导类型不透明别名模板是透明的(等价于原名)
模板模板参数的 class/typenametemplate<template<class> typename C>必须是 class(C++17 前),C++17 起可用 typename
忘记 typename 在依赖名中编译错误依赖类型的别名前加 typename

常见面试问题

  1. C++11 的 using 别名和 typedef 的区别?——using 支持模板别名(template<typename T> using Vec = std::vector<T>),typedef 不能。非模板场景二者几乎等价,但 using 语法更直观。

  2. 什么是模板模板参数?什么时候使用?——允许模板接受另一个模板作为参数。用于策略模式——编译期切换底层实现(如 STL 的容器适配器 stack 接受容器作为模板参数)。

  3. 别名的类型安全性?——using 别名是透明的(type alias),不是新类型。using Age = int; 后 Age 和 int 完全等价,可直接互相赋值。要强类型需用包装类。

  4. C++17 前模板模板参数匹配 std::vector 有什么问题?——std::vector 声明为 template<class T, class Alloc>,有两个模板参数而传统的模板模板参数只接受一个。C++17 后可用参数包匹配解决。

  5. std::enable_if 和别名模板的结合使用?——template<typename T> using EnableIfIntegral = std::enable_if_t<std::is_integral_v<T>, T>;——简化 SFINAE 代码,提高可读性。

小结

  • using 别名模板(C++11)是 typedef 的泛型升级——支持参数化别名
  • 模板模板参数实现编译期策略模式——在容器适配器等场景取代运行时多态
  • C++17 参数包匹配:template<typename...> class C 可匹配任意参数数量的类模板
  • 别名模板是透明的——不创建新类型,只创建新名字
  • std::type_traits 中大量使用别名模板简化名称:std::enable_if_t、std::remove_reference_t 等
上一页
可变参数模板
下一页
SFINAE 与类型萃取