|
| 1 | +<div align=right> |
| 2 | + |
| 3 | + 🌎 [中文] | [English] |
| 4 | +</div> |
| 5 | + |
| 6 | +[中文]: ../../cpp14/00-generic-lambdas.html |
| 7 | +[English]: ./00-generic-lambdas.html |
| 8 | + |
| 9 | +# Generic Lambdas |
| 10 | + |
| 11 | +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 |
| 12 | + |
| 13 | +| Book | Video | Code | X | |
| 14 | +| --- | --- | --- | --- | |
| 15 | +| [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) | | |
| 16 | + |
| 17 | + |
| 18 | +**Why introduced?** |
| 19 | + |
| 20 | +- 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 |
| 21 | +- 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 |
| 22 | +- C++14 allows `auto` in lambda parameters; the compiler generates an implicit template for `operator()`, essentially bringing function templates into the lambda world |
| 23 | + |
| 24 | +**How does it work?** |
| 25 | + |
| 26 | +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: |
| 27 | + |
| 28 | +```cpp |
| 29 | +struct __lambda { |
| 30 | + template <typename T1, typename T2> |
| 31 | + auto operator()(T1 a, T2 b) const { |
| 32 | + return a + b; |
| 33 | + } |
| 34 | +}; |
| 35 | +``` |
| 36 | +
|
| 37 | +## I. Basic Usage and Scenarios |
| 38 | +
|
| 39 | +### Simple Generic Lambda |
| 40 | +
|
| 41 | +> Declare lambda parameters with auto; the compiler generates operator() instances based on the argument types at the call site |
| 42 | +
|
| 43 | +```cpp |
| 44 | +auto identity = [](auto x) { |
| 45 | + return x; |
| 46 | +}; |
| 47 | +
|
| 48 | +int i = identity(42); // x deduced as int |
| 49 | +double d = identity(3.14); // x deduced as double |
| 50 | +``` |
| 51 | + |
| 52 | +### Multi-Parameter Generic Lambda |
| 53 | + |
| 54 | +```cpp |
| 55 | +auto add = [](auto a, auto b) { |
| 56 | + return a + b; |
| 57 | +}; |
| 58 | + |
| 59 | +add(1, 2); // int + int |
| 60 | +add(1.5, 2.5); // double + double |
| 61 | +add(std::string("hello "), std::string("world")); // string + string |
| 62 | +``` |
| 63 | +
|
| 64 | +Each parameter's type is deduced independently; `T1` and `T2` can differ: |
| 65 | +
|
| 66 | +```cpp |
| 67 | +auto multiply = [](auto a, auto b) { |
| 68 | + return a * b; |
| 69 | +}; |
| 70 | +
|
| 71 | +multiply(2, 3.5); // int * double → double |
| 72 | +``` |
| 73 | + |
| 74 | +### Generic Lambda + STL Algorithms |
| 75 | + |
| 76 | +The most common use case — avoid writing identical logic for every container element type: |
| 77 | + |
| 78 | +```cpp |
| 79 | +std::vector<int> v1 = {5, 1, 4, 2, 8}; |
| 80 | +std::vector<double> v2 = {3.1, 2.7, 8.5, 1.9}; |
| 81 | + |
| 82 | +// C++11: write separate lambdas for int and double |
| 83 | +std::sort(v1.begin(), v1.end(), [](int a, int b) { return a > b; }); |
| 84 | +std::sort(v2.begin(), v2.end(), [](double a, double b) { return a > b; }); |
| 85 | + |
| 86 | +// C++14: a single generic lambda handles both |
| 87 | +auto gt = [](auto a, auto b) { return a > b; }; |
| 88 | +std::sort(v1.begin(), v1.end(), gt); |
| 89 | +std::sort(v2.begin(), v2.end(), gt); |
| 90 | +``` |
| 91 | +
|
| 92 | +### Generic Lambda with Captures |
| 93 | +
|
| 94 | +Captured variables keep their concrete types; only parameters use `auto`: |
| 95 | +
|
| 96 | +```cpp |
| 97 | +int threshold = 10; |
| 98 | +auto above = [threshold](auto x) { |
| 99 | + return x > threshold; // threshold is int, x is generic |
| 100 | +}; |
| 101 | +
|
| 102 | +above(20); // x = int |
| 103 | +above(3.5); // x = double |
| 104 | +``` |
| 105 | + |
| 106 | +### Generic Lambda Returning Lambda |
| 107 | + |
| 108 | +A generic lambda can return a new lambda, creating a function factory: |
| 109 | + |
| 110 | +```cpp |
| 111 | +auto make_adder = [](auto n) { |
| 112 | + return [n](auto x) { return x + n; }; // C++14 supports this |
| 113 | +}; |
| 114 | + |
| 115 | +auto add5 = make_adder(5); |
| 116 | +add5(10); // 15 |
| 117 | +add5(3.14); // 8.14 |
| 118 | +``` |
| 119 | +
|
| 120 | +## II. Notes |
| 121 | +
|
| 122 | +### A Generic Lambda Is a Class with a Templated operator() |
| 123 | +
|
| 124 | +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 |
| 125 | +
|
| 126 | +```cpp |
| 127 | +auto f = [](auto x) { return x; }; |
| 128 | +auto g = [](auto x) { return x; }; |
| 129 | +// f and g have different types; cannot be assigned to each other |
| 130 | +``` |
| 131 | + |
| 132 | +### Generic Parameters and Perfect Forwarding |
| 133 | + |
| 134 | +Generic lambda parameter deduction strips references and const by default. Use `auto&&` with `std::forward` to preserve them: |
| 135 | + |
| 136 | +```cpp |
| 137 | +auto forwarder = [](auto&& x) -> decltype(auto) { |
| 138 | + return std::forward<decltype(x)>(x); |
| 139 | +}; |
| 140 | +``` |
| 141 | +
|
| 142 | +This pattern is common with generic lambdas and is a typical use case for `decltype(auto)` (another C++14 feature) |
| 143 | +
|
| 144 | +### Generic Lambdas Are Not Variadic |
| 145 | +
|
| 146 | +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) |
| 147 | +
|
| 148 | +## III. Exercise Code |
| 149 | +
|
| 150 | +### Exercise Code Topics |
| 151 | +
|
| 152 | +- 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) |
| 153 | +- 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) |
| 154 | +
|
| 155 | +### Exercise Auto-Checker Command |
| 156 | +
|
| 157 | +``` |
| 158 | +d2x checker generic-lambdas |
| 159 | +``` |
| 160 | +
|
| 161 | +## IV. Other |
| 162 | +
|
| 163 | +- [Discussion Forum](https://forum.d2learn.org/category/20) |
| 164 | +- [d2mcpp Tutorial Repository](https://github.com/mcpp-community/d2mcpp) |
| 165 | +- [Tutorial Video List](https://space.bilibili.com/65858958/lists/5208246) |
| 166 | +- [Tutorial Support Tool - xlings](https://github.com/openxlings/xlings) |
0 commit comments