【C++14】(一)Generic Lambda 与 Variable Template:现代C++的起点
C++14 是现代 C++ 的真正起点——它让模板和 Lambda 不再是”奇技淫巧”,而是日常编程的一部分。
前言
C++11 引入了 Lambda,C++14 则让它真正变得通用。配合 Variable Template,C++14 开启了一个新纪元:编译期计算和泛型编程终于可以写得优雅而直观。
读完本文,你会理解:
- Generic Lambda 的本质是模板语法糖
- Variable Template 如何实现”一次定义,多态使用”
std::make_unique为什么优于裸newstd::index_sequence如何让 Tuple 解包成为可能
一、Generic Lambda:模板方法的语法糖
1.1 什么是 Generic Lambda?
1 | auto add = [](auto a, auto b) { return a + b; }; |
这行代码看似简单,实则编译器会把它翻译成一个带有模板 operator() 的匿名类:
1 | struct __AddLambda { |
核心原理:每个 auto 参数对应模板参数的一次实例化。换句话说:
1 | [](auto a, auto b) { return a + b; } |
等价于:
1 | template<typename A, typename B> |
1.2 Mermaid 图:Generic Lambda 编译过程
graph LR
A["👤 用户代码<br/>`auto add = [](auto a, auto b) { ... };`"]
B["🔧 编译器生成<br/>模板类"]
C["📦 实例化1<br/>`add(1, 2)` → int"]
D["📦 实例化2<br/>`add(3.14, 2.86)` → double"]
E["📦 实例化3<br/>`add(string, string)` → string"]
A --> B
B --> C
B --> D
B --> E
style A fill:#C7CEEA,stroke:#9FA8DA,stroke-width:2px,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,stroke-width:2px,color:#333
style C fill:#B5EAD7,stroke:#80CBC4,stroke-width:2px,color:#333
style D fill:#B5EAD7,stroke:#80CBC4,stroke-width:2px,color:#333
style E fill:#B5EAD7,stroke:#80CBC4,stroke-width:2px,color:#3331.3 Lambda 的本质:无法 decltype
Lambda 没有名字,无法直接 decltype。但可以用 std::function 包装:
1 | auto my_lambda = [](int x) { return x * 2; }; |
这与 Lambda 的”匿名性”本质相关——编译器生成的类型是一个未命名的仿函数,语法上无法写出它的名字。
二、Variable Template:一次定义,多态使用
2.1 基本用法
1 | template<typename T> |
与 constexpr 变量的区别:
| 维度 | constexpr 变量 | Variable Template |
|---|---|---|
| 本质 | 编译期常量 | 模板,每个实例化都是独立变量 |
| 参数化 | 无 | 按类型或整数值参数化 |
| 示例 | constexpr int MAX = 100; | template<int N> constexpr int factorial = N * factorial<N-1>; |
2.2 Mermaid 图:Variable Template 实例化
graph TB
A["📝 单一定义<br/>`template<typename T> constexpr T pi = ...`"]
B["📦 实例化: int<br/>`pi<int>` → 3"]
C["📦 实例化: double<br/>`pi<double>` → 3.14159..."]
D["📦 实例化: float<br/>`pi<float>` → 3.14159f"]
A --> B
A --> C
A --> D
style A fill:#FFF9C4,stroke:#F9A825,stroke-width:2px,color:#333
style B fill:#B5EAD7,stroke:#80CBC4,stroke-width:2px,color:#333
style C fill:#B5EAD7,stroke:#80CBC4,stroke-width:2px,color:#333
style D fill:#B5EAD7,stroke:#80CBC4,stroke-width:2px,color:#3332.3 编译期阶乘:递归实例化的威力
1 | template<int N> |
这利用了模板的递归实例化特性——每个 factorial<N> 都依赖 factorial<N-1>,直到 base case factorial<0>。
三、std::make_unique:比 new 更安全
3.1 异常安全
1 | auto p1 = std::make_unique<int>(42); // ✅ 异常安全 |
为什么更安全?
考虑以下场景:
1 | void process() { |
3.2 make_unique for 数组
1 | auto arr = std::make_unique<int[]>(10); // ✅ C++14 支持 |
注意:
std::make_unique<T[]>(n)在 C++14 引入,C++20 才开始支持默认初始化std::make_unique<int[]>(10, 0)。
四、std::index_sequence:解包 Tuple 的钥匙
4.1 问题背景
如何遍历一个 std::tuple 的所有元素?传统方法需要递归模板:
1 | template<typename... Args> |
这里用到了一个 折叠表达式(fold expression) 技巧:(expression)... 会展开为 expr1, expr2, ...。
4.2 index_sequence 的典型用法
1 | template<typename Tuple, std::size_t... Is> |
std::index_sequence 本质上是一个编译期整数序列,用于在模板元编程中展开参数包。
五、完整可运行代码
1 |
|
编译验证:
1 | g++ -std=c++14 -o demo demo.cpp && ./demo |
总结
| 特性 | 核心价值 | 使用场景 |
|---|---|---|
| Generic Lambda | 模板参数推导自动化 | 泛型算法、回调函数 |
| Variable Template | 类型参数化变量 | 数学常量、编译期查表 |
| make_unique | 异常安全 + 简洁语法 | 替代所有 new 表达式 |
| index_sequence | 参数包展开工具 | Tuple/ParameterPack 处理 |
行动建议:从今天开始,写代码时优先使用
std::make_unique替代new/delete;需要泛型 Lambda 时,直接用auto参数,无需显式声明模板。
📚 C++14 新特性 系列导航
本文是《C++14 新特性》系列第 1/2 篇。
| 方向 | 章节 |
|---|---|
| 下一篇 ▶ | (二)Binary Literals 与 Deprecated |