Cpp lambda and bind
基本表达式
[capture](parameters) -> return-type {body}当没有返回类型时, 可以省略 -> return-type
变量捕获与lambda闭包实现
[]不截取任何变量[&]截取外部作用域中所有变量,并作为引用在函数体中使用[=]截取外部作用域中所有变量,并拷贝一份在函数体中使用[=, &foo]截取外部作用域中所有变量,并拷贝一份在函数体中使用,但是对foo变量使用引用[bar]截取bar变量并且拷贝一份在函数体中使用,同时不截取其他变量[x, &y]x按值传递,y按引用传递[this]截取当前类中的this指针。如果已经使用了&或者=就默认添加此选项。
lambda 的底层实现
lambda 其实是c++的语法糖,是通过c++编译器生成class来实现的,看一个简单的例子。
#include <functional>
using namespace std;
int main(int argc, char* argv[]) {
int x = 0;
int y = 0;
int z = 0;
auto func = [&](int a)->int{x+=a; y++; return x;};
func(1);
return 0;
}通过cppinsights 站点来查看编译器的实现方法
#include <functional>
using namespace std;
int main(int argc, char** argv) {
int x = 0;
int y = 0;
int z = 0;
class __lambda_8_16 {
public:
inline /*constexpr */ int operator()(int a) const {
x = x + a;
y++;
return x;
}
private:
int& x;
int& y;
public:
__lambda_8_16(int& _x, int& _y) : x{_x}, y{_y} {}
};
__lambda_8_16 func = __lambda_8_16{x, y};
static_cast<const __lambda_8_16>(func).operator()(1);
return 0;
}匿名函数在编译器中生成了类 class __lambda_6_16,其中operator()中使用了x和y,参数是引用传参。
lambda 表达式中需要注意的事项
1. mutable 关键字
默认情况下,按值捕获的变量在lambda函数体内是const的,不能修改。使用mutable关键字可以移除这个const限制:
int x = 0;
auto func = [x]() mutable {
x++; // 可以修改,但修改的是副本
return x;
};2. 泛型lambda (C++14)
C++14引入了泛型lambda,可以使用auto作为参数类型:
auto add = [](auto a, auto b) { return a + b; };
std::cout << add(1, 2) << std::endl; // 3
std::cout << add(1.5, 2.5) << std::endl; // 4.03. 捕获时初始化 (C++14)
可以在捕获列表中初始化变量,这对于捕获只能移动的类型特别有用:
std::unique_ptr<int> ptr = std::make_unique<int>(42);
auto func = [value = std::move(ptr)]() {
return *value;
};4. 模板lambda (C++20)
C++20引入了模板lambda,可以使用模板语法:
auto func = []<typename T>(T a, T b) { return a + b; };
std::cout << func(1, 2) << std::endl;5. constexpr lambda (C++17)
C++17允许lambda在编译时求值:
constexpr auto square = [](int n) { return n * n; };
static_assert(square(5) == 25);6. this捕获的变化 (C++20)
C++20中,[=]不再隐式捕获this,需要显式捕获:
class MyClass {
int value = 42;
public:
auto getFunc() {
// C++20: 需要显式捕获this
return [=, this]() { return value; };
// 或者使用 [*this] 按值捕获整个对象
}
};7. 生命周期问题
捕获引用时需要特别注意生命周期,避免悬空引用:
std::function<int()> createFunc() {
int x = 42;
// 危险:x在函数返回后被销毁
return [&x]() { return x; };
}
// 正确做法:按值捕获
std::function<int()> createFuncSafe() {
int x = 42;
return [x]() { return x; };
}8. 性能考虑
- lambda通常会被编译器内联,性能接近普通函数
- 避免在性能关键路径中使用复杂的捕获
- 小lambda适合作为算法参数,如
std::sort的比较函数
9. 与std::bind的比较
优先使用lambda而不是std::bind,因为:
- lambda语法更清晰
- 编译器优化更好
- 类型安全更强
- C++14后lambda功能更强大
10. 转换为函数指针
无捕获的lambda可以隐式转换为函数指针:
void callFunc(int (*func)(int)) {
std::cout << func(5) << std::endl;
}
int main() {
auto lambda = [](int x) { return x * 2; };
callFunc(lambda); // 可以转换
}11. lambda参数特性
lambda的参数语法与普通函数类似,但有一些特殊之处:
默认参数
lambda支持默认参数,但需要注意使用场景:
auto greet = [](const std::string& name = "World") {
std::cout << "Hello, " << name << "!" << std::endl;
};
greet(); // Hello, World!
greet("Alice"); // Hello, Alice!引用参数和const引用
可以像普通函数一样使用引用参数:
auto swapValues = [](int& a, int& b) {
int temp = a;
a = b;
b = temp;
};
int x = 5, y = 10;
swapValues(x, y); // x=10, y=5可变参数模板 (C++14)
lambda支持可变参数模板:
auto printAll = [](auto&&... args) {
(std::cout << ... << args) << std::endl;
};
printAll(1, " + ", 2, " = ", 3); // 输出: 1 + 2 = 3参数类型推导
C++14引入的泛型lambda可以自动推导参数类型:
// 自动推导参数类型
auto maxValue = [](auto a, auto b) {
return a > b ? a : b;
};
std::cout << maxValue(3.14, 2.71) << std::endl; // 3.14
std::cout << maxValue(5, 10) << std::endl; // 10参数包展开
可以在lambda体内展开参数包:
auto sumAll = [](auto... args) {
return (args + ...); // 折叠表达式
};
std::cout << sumAll(1, 2, 3, 4, 5) << std::endl; // 15noexcept规范
可以为lambda指定noexcept:
auto safeDivide = [](int a, int b) noexcept -> int {
if (b == 0) return 0; // 注意:noexcept函数中抛出异常会终止程序
return a / b;
};属性说明符 (C++11/14/17)
可以为lambda添加属性说明符:
// [[nodiscard]] 属性 (C++17)
auto createResource = []() [[nodiscard]] {
return std::make_unique<int>(42);
};
// [[deprecated]] 属性
auto oldFunc = []() [[deprecated("Use newFunc instead")]] {
return 42;
};12. 递归lambda
lambda不能直接递归调用自己,需要使用std::function或Y组合子:
// 使用std::function
std::function<int(int)> factorial;
factorial = [&factorial](int n) -> int {
return n <= 1 ? 1 : n * factorial(n - 1);
};
// 使用Y组合子(高级技巧)
auto y = [](auto f) {
return [=](auto... args) {
return f(f, args...);
};
};
auto factorial2 = y([](auto self, int n) -> int {
return n <= 1 ? 1 : n * self(self, n - 1);
});








