泛型 lambda 是 C++14 在 lambda 表达式上引入的一项重要增强, 允许 lambda 的参数使用 auto 类型推导, 使得一个 lambda 表达式可以像函数模板一样处理不同类型的参数
| Book | Video | Code | X |
|---|---|---|---|
| cppreference-lambda / markdown | 视频解读 | 练习代码 |
为什么引入?
- 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; } 在内部等价于:
struct __lambda {
template <typename T1, typename T2>
auto operator()(T1 a, T2 b) const {
return a + b;
}
};lambda 参数用 auto 声明, 编译器根据调用时的实参类型生成对应的 operator() 实例
auto identity = [](auto x) {
return x;
};
int i = identity(42); // x 推导为 int
double d = identity(3.14); // x 推导为 doubleauto 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 可以不同:
auto multiply = [](auto a, auto b) {
return a * b;
};
multiply(2, 3.5); // int * double → double泛型 lambda 最常见的应用场景是配合 STL 算法, 避免为每种容器元素类型重复编写相同逻辑的比较/判据:
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);捕获的变量类型不变, 只有参数用 auto:
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, 实现类似"函数工厂"的效果:
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每个泛型 lambda 表达式产生一个独立的闭包类型。即使是写法完全相同的两个泛型 lambda, 它们的类型也不同 — 这和普通 lambda 的规则一样, 但泛型 lambda 的 operator() 是模板, 所以同一类型可以接受不同参数类型
auto f = [](auto x) { return x; };
auto g = [](auto x) { return x; };
// f 和 g 的类型不同, 不能互相赋值泛型 lambda 的参数推导默认剥离引用和 const, 需要完整保留时用 auto&& 配合 std::forward:
auto forwarder = [](auto&& x) -> decltype(auto) {
return std::forward<decltype(x)>(x);
};这种写法在泛型 lambda 里很常见, 也是 decltype(auto) (C++14 的另一特性) 的典型使用场景
泛型 lambda 的参数个数仍然是固定的 — [](auto a, auto b) 接受恰好两个参数。如果要变参, 仍然需要可变参数模板 (C++20 后才支持 lambda 中使用 ... 参数包)
d2x checker generic-lambdas