-
Notifications
You must be signed in to change notification settings - Fork 110
docs(cpp14): add 00-generic-lambdas book chapter, exercises, solutions, and build wiring #71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Sunrisepeak
merged 4 commits into
mcpp-community:main
from
lczllx:feat/cpp14-00-generic-lambdas
Jun 17, 2026
Merged
Changes from 2 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
c387004
docs(cpp14): add 00-generic-lambdas book chapter and build wiring
lczllx 3fa0d39
feat(cpp14): add 00-generic-lambdas exercises and solutions
lczllx 4245822
docs(cpp14): add STL real-world case and d2x setup block to 00-generi…
lczllx de16c08
docs(cpp14): fix Copilot review findings in 00-generic-lambdas
lczllx File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| **现代C++标准(cppref)** | ||
|
|
||
| - C++11: [中](https://zh.cppreference.com/w/cpp/11) / [En](https://en.cppreference.com/w/cpp/11) | ||
| - C++14: [中](https://zh.cppreference.com/w/cpp/14) / [En](https://en.cppreference.com/w/cpp/14) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,166 @@ | ||
| <div align=right> | ||
|
|
||
| 🌎 [中文] | [English] | ||
| </div> | ||
|
|
||
| [中文]: ../../cpp14/00-generic-lambdas.html | ||
| [English]: ./00-generic-lambdas.html | ||
|
|
||
| # Generic Lambdas | ||
|
|
||
| C++14 allows lambda parameters to use `auto`, turning the lambda's `operator()` into an implicit function template — a single lambda can accept arguments of different types | ||
|
|
||
| | Book | Video | Code | X | | ||
| | --- | --- | --- | --- | | ||
| | [cppreference-lambda](https://en.cppreference.com/w/cpp/language/lambda) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp14/00-generic-lambdas.md) | [Video Explanation]() | [Exercise Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp14/00-generic-lambdas-0.cpp) | | | ||
|
|
||
|
|
||
| **Why introduced?** | ||
|
|
||
| - In C++11, lambda parameter types must be explicitly specified — the same lambda cannot be reused for different argument types because `operator()` is a plain member function, not a template | ||
| - Many lambdas express type-independent logic (e.g. `[](auto a, auto b) { return a < b; }` works for `int`, `double`, `string`), but C++11 required writing a separate lambda for each type | ||
| - C++14 allows `auto` in lambda parameters; the compiler generates an implicit template for `operator()`, essentially bringing function templates into the lambda world | ||
|
|
||
| **How does it work?** | ||
|
|
||
| The compiler expands a generic lambda into a functor class with a **templated `operator()`**. For example, `[](auto a, auto b) { return a + b; }` is internally equivalent to: | ||
|
|
||
| ```cpp | ||
| struct __lambda { | ||
| template <typename T1, typename T2> | ||
| auto operator()(T1 a, T2 b) const { | ||
| return a + b; | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
| ## I. Basic Usage and Scenarios | ||
|
|
||
| ### Simple Generic Lambda | ||
|
|
||
| > Declare lambda parameters with auto; the compiler generates operator() instances based on the argument types at the call site | ||
|
|
||
| ```cpp | ||
| auto identity = [](auto x) { | ||
| return x; | ||
| }; | ||
|
|
||
| int i = identity(42); // x deduced as int | ||
| double d = identity(3.14); // x deduced as double | ||
| ``` | ||
|
|
||
| ### Multi-Parameter Generic Lambda | ||
|
|
||
| ```cpp | ||
| auto add = [](auto a, auto b) { | ||
| return a + b; | ||
| }; | ||
|
|
||
| add(1, 2); // int + int | ||
| add(1.5, 2.5); // double + double | ||
| add(std::string("hello "), std::string("world")); // string + string | ||
| ``` | ||
|
|
||
| Each parameter's type is deduced independently; `T1` and `T2` can differ: | ||
|
|
||
| ```cpp | ||
| auto multiply = [](auto a, auto b) { | ||
| return a * b; | ||
| }; | ||
|
|
||
| multiply(2, 3.5); // int * double → double | ||
| ``` | ||
|
|
||
| ### Generic Lambda + STL Algorithms | ||
|
|
||
| The most common use case — avoid writing identical logic for every container element type: | ||
|
|
||
| ```cpp | ||
| std::vector<int> v1 = {5, 1, 4, 2, 8}; | ||
| std::vector<double> v2 = {3.1, 2.7, 8.5, 1.9}; | ||
|
|
||
| // C++11: write separate lambdas for int and double | ||
| std::sort(v1.begin(), v1.end(), [](int a, int b) { return a > b; }); | ||
| std::sort(v2.begin(), v2.end(), [](double a, double b) { return a > b; }); | ||
|
|
||
| // C++14: a single generic lambda handles both | ||
| auto gt = [](auto a, auto b) { return a > b; }; | ||
| std::sort(v1.begin(), v1.end(), gt); | ||
| std::sort(v2.begin(), v2.end(), gt); | ||
| ``` | ||
|
|
||
| ### Generic Lambda with Captures | ||
|
|
||
| Captured variables keep their concrete types; only parameters use `auto`: | ||
|
|
||
| ```cpp | ||
| int threshold = 10; | ||
| auto above = [threshold](auto x) { | ||
| return x > threshold; // threshold is int, x is generic | ||
| }; | ||
|
|
||
| above(20); // x = int | ||
| above(3.5); // x = double | ||
| ``` | ||
|
|
||
| ### Generic Lambda Returning Lambda | ||
|
|
||
| A generic lambda can return a new lambda, creating a function factory: | ||
|
|
||
| ```cpp | ||
| auto make_adder = [](auto n) { | ||
| return [n](auto x) { return x + n; }; // C++14 supports this | ||
| }; | ||
|
|
||
| auto add5 = make_adder(5); | ||
| add5(10); // 15 | ||
| add5(3.14); // 8.14 | ||
| ``` | ||
|
|
||
| ## II. Notes | ||
|
|
||
| ### A Generic Lambda Is a Class with a Templated operator() | ||
|
|
||
| Each generic lambda expression produces a distinct closure type. Even two identical-looking generic lambdas have different types — the same rule as regular lambdas, but the `operator()` is a template, so the same type can accept different argument types | ||
|
|
||
| ```cpp | ||
| auto f = [](auto x) { return x; }; | ||
| auto g = [](auto x) { return x; }; | ||
| // f and g have different types; cannot be assigned to each other | ||
| ``` | ||
|
|
||
| ### Generic Parameters and Perfect Forwarding | ||
|
|
||
| Generic lambda parameter deduction strips references and const by default. Use `auto&&` with `std::forward` to preserve them: | ||
|
|
||
| ```cpp | ||
| auto forwarder = [](auto&& x) -> decltype(auto) { | ||
| return std::forward<decltype(x)>(x); | ||
| }; | ||
| ``` | ||
|
|
||
| This pattern is common with generic lambdas and is a typical use case for `decltype(auto)` (another C++14 feature) | ||
|
|
||
| ### Generic Lambdas Are Not Variadic | ||
|
|
||
| The parameter count is still fixed — `[](auto a, auto b)` accepts exactly two arguments. For variadic parameters, you still need variadic templates (C++20 later added support for `...` parameter packs in lambdas) | ||
|
lczllx marked this conversation as resolved.
Outdated
|
||
|
|
||
| ## III. Exercise Code | ||
|
|
||
| ### Exercise Code Topics | ||
|
|
||
| - 0 - [Basic Generic Lambda — auto parameters and type deduction](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp14/00-generic-lambdas-0.cpp) | ||
| - 1 - [Generic Lambda with STL Algorithms — sort, find, factory function](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp14/00-generic-lambdas-1.cpp) | ||
|
|
||
| ### Exercise Auto-Checker Command | ||
|
|
||
| ``` | ||
| d2x checker generic-lambdas | ||
| ``` | ||
|
|
||
| ## IV. Other | ||
|
|
||
| - [Discussion Forum](https://forum.d2learn.org/category/20) | ||
| - [d2mcpp Tutorial Repository](https://github.com/mcpp-community/d2mcpp) | ||
| - [Tutorial Video List](https://space.bilibili.com/65858958/lists/5208246) | ||
| - [Tutorial Support Tool - xlings](https://github.com/openxlings/xlings) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,166 @@ | ||
| <div align=right> | ||
|
|
||
| 🌎 [中文] | [English] | ||
| </div> | ||
|
|
||
| [中文]: ./00-generic-lambdas.html | ||
| [English]: ../en/cpp14/00-generic-lambdas.html | ||
|
|
||
| # 泛型 lambda - generic lambdas | ||
|
|
||
| 泛型 lambda 是 C++14 在 lambda 表达式上引入的一项重要增强, 允许 lambda 的参数使用 `auto` 类型推导, 使得一个 lambda 表达式可以像函数模板一样处理不同类型的参数 | ||
|
|
||
| | Book | Video | Code | X | | ||
| | --- | --- | --- | --- | | ||
| | [cppreference-lambda](https://en.cppreference.com/w/cpp/language/lambda) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp14/00-generic-lambdas.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp14/00-generic-lambdas-0.cpp) | | | ||
|
|
||
|
|
||
| **为什么引入?** | ||
|
|
||
| - C++11 的 lambda 参数类型必须显式指定, 同一个 lambda 无法复用于不同类型的参数 — 本质上 lambda 的 `operator()` 是一个普通成员函数, 不能是模板 | ||
| - 在实际编码中, 很多 lambda 的逻辑与类型无关 (例如 `[](int a, int b) { return a < b; }` 中的比较逻辑对 `int` / `double` / `string` 都适用), 但 C++11 要求为每种类型写一份 | ||
| - C++14 允许 lambda 参数使用 `auto`, 编译器会为 `operator()` 生成一个隐式的模板, 每种参数类型实例化一份 — 相当于把"函数模板"带到了 lambda 世界里 | ||
|
|
||
| **泛型 lambda 是如何实现的?** | ||
|
|
||
| 编译器将泛型 lambda 展开为一个带有**模板化 `operator()`** 的仿函数类。例如 `[](auto a, auto b) { return a + b; }` 在内部等价于: | ||
|
|
||
| ```cpp | ||
| struct __lambda { | ||
| template <typename T1, typename T2> | ||
| auto operator()(T1 a, T2 b) const { | ||
| return a + b; | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
| ## 一、基础用法和场景 | ||
|
|
||
| ### 简单泛型 lambda | ||
|
|
||
| > lambda 参数用 auto 声明, 编译器根据调用时的实参类型生成对应的 operator() 实例 | ||
|
|
||
| ```cpp | ||
| auto identity = [](auto x) { | ||
| return x; | ||
| }; | ||
|
|
||
| int i = identity(42); // x 推导为 int | ||
| double d = identity(3.14); // x 推导为 double | ||
| ``` | ||
|
|
||
| ### 多参数泛型 lambda | ||
|
|
||
| ```cpp | ||
| auto add = [](auto a, auto b) { | ||
| return a + b; | ||
| }; | ||
|
|
||
| add(1, 2); // int + int | ||
| add(1.5, 2.5); // double + double | ||
| add(std::string("hello "), std::string("world")); // string + string | ||
| ``` | ||
|
|
||
| 每个参数的类型是独立推导的, `T1` 和 `T2` 可以不同: | ||
|
|
||
| ```cpp | ||
| auto multiply = [](auto a, auto b) { | ||
| return a * b; | ||
| }; | ||
|
|
||
| multiply(2, 3.5); // int * double → double | ||
| ``` | ||
|
|
||
| ### 泛型 lambda + STL 算法 | ||
|
|
||
| 泛型 lambda 最常见的应用场景是配合 STL 算法, 避免为每种容器元素类型重复编写相同逻辑的比较/判据: | ||
|
|
||
| ```cpp | ||
| std::vector<int> v1 = {5, 1, 4, 2, 8}; | ||
| std::vector<double> v2 = {3.1, 2.7, 8.5, 1.9}; | ||
|
|
||
| // C++11: 需要为 int 和 double 分别写 lambda | ||
| std::sort(v1.begin(), v1.end(), [](int a, int b) { return a > b; }); | ||
| std::sort(v2.begin(), v2.end(), [](double a, double b) { return a > b; }); | ||
|
|
||
| // C++14: 同一个泛型 lambda 搞定 | ||
| auto gt = [](auto a, auto b) { return a > b; }; | ||
| std::sort(v1.begin(), v1.end(), gt); | ||
| std::sort(v2.begin(), v2.end(), gt); | ||
| ``` | ||
|
|
||
| ### 带捕获的泛型 lambda | ||
|
|
||
| 捕获的变量类型不变, 只有参数用 `auto`: | ||
|
|
||
| ```cpp | ||
| int threshold = 10; | ||
| auto above = [threshold](auto x) { | ||
| return x > threshold; // threshold 捕获为 int, x 是泛型参数 | ||
| }; | ||
|
|
||
| above(20); // x = int | ||
| above(3.5); // x = double | ||
| ``` | ||
|
|
||
| ### 泛型 lambda 返回 lambda | ||
|
|
||
| 泛型 lambda 可以返回一个新 lambda, 实现类似"函数工厂"的效果: | ||
|
|
||
| ```cpp | ||
| auto make_adder = [](auto n) { | ||
| return [n](auto x) { return x + n; }; // C++14 支持, 返回类型自动推导 | ||
| }; | ||
|
|
||
| auto add5 = make_adder(5); | ||
| add5(10); // 15 | ||
| add5(3.14); // 8.14 | ||
| ``` | ||
|
|
||
| ## 二、注意事项 | ||
|
lczllx marked this conversation as resolved.
Outdated
|
||
|
|
||
| ### 泛型 lambda 是一个有模板 operator() 的类 | ||
|
|
||
| 每个泛型 lambda 表达式产生一个独立的闭包类型。即使是写法完全相同的两个泛型 lambda, 它们的类型也不同 — 这和普通 lambda 的规则一样, 但泛型 lambda 的 `operator()` 是模板, 所以同一类型可以接受不同参数类型 | ||
|
|
||
| ```cpp | ||
| auto f = [](auto x) { return x; }; | ||
| auto g = [](auto x) { return x; }; | ||
| // f 和 g 的类型不同, 不能互相赋值 | ||
| ``` | ||
|
|
||
| ### 泛型参数和完美转发 | ||
|
|
||
| 泛型 lambda 的参数推导默认剥离引用和 const, 需要完整保留时用 `auto&&` 配合 `std::forward`: | ||
|
|
||
| ```cpp | ||
| auto forwarder = [](auto&& x) -> decltype(auto) { | ||
| return std::forward<decltype(x)>(x); | ||
| }; | ||
| ``` | ||
|
|
||
| 这种写法在泛型 lambda 里很常见, 也是 `decltype(auto)` (C++14 的另一特性) 的典型使用场景 | ||
|
|
||
| ### 泛型 lambda 不是 variadic | ||
|
|
||
| 泛型 lambda 的参数个数仍然是固定的 — `[](auto a, auto b)` 接受恰好两个参数。如果要变参, 仍然需要可变参数模板 (C++20 后才支持 lambda 中使用 `...` 参数包) | ||
|
|
||
|
|
||
| ## 三、练习代码 | ||
|
|
||
| ### 练习代码主题 | ||
|
|
||
| - 0 - [泛型 lambda 基础 — auto 参数与类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp14/00-generic-lambdas-0.cpp) | ||
| - 1 - [泛型 lambda 与 STL 算法 — 排序、查找、函数工厂](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp14/00-generic-lambdas-1.cpp) | ||
|
|
||
| ### 练习代码自动检测命令 | ||
|
|
||
|
lczllx marked this conversation as resolved.
|
||
| ``` | ||
| d2x checker generic-lambdas | ||
| ``` | ||
|
|
||
| ## 四、其他 | ||
|
|
||
| - [交流讨论](https://forum.d2learn.org/category/20) | ||
| - [d2mcpp教程仓库](https://github.com/mcpp-community/d2mcpp) | ||
| - [教程视频列表](https://space.bilibili.com/65858958/lists/5208246) | ||
| - [教程支持工具-xlings](https://github.com/openxlings/xlings) | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.