C++ lambda表达式
C++11 中有两种捕获模式: 按引用和按值。按引用的默认捕获模式可能导致空悬引用,按值的默认捕获模式也无法对空悬引用免疫,而且会让你认为你的闭包是独立的(事实上它们可能不是独立的)。
按引用捕获会导致闭包包含指涉到局部变量的引用,或者指涉到定义lambda
式的作用域内的形参的引用。一旦由lambda
式所创建的闭包越过了该局部变量或形参的生命周期,那么闭包内的引用就会空悬。
std::vector<std::function<int(int)>> vec;
int f() {
int divisor = 0;
vec.push_back([&](int val){ return val / divisor;});
}
int f2() {
auto lamdba = vec.at(0);
lambda(1); ///< 此处可能会出现空悬引用。
}
你可能会觉得如果把上面的按引用捕获换为按值捕获这样的空悬引用就不会出现。
std::vector<std::function<int(int)>> vec;
class object {
public:
int f() {
int divisor = 0;
vec.push_back([=](int val){ return val / divisor;}); ///< 这里是引用捕获
}
private:
int divisor = 0;
}
引用捕获只能针对于在创建lambda
式的作用域内可见的非静态局部变量(包括形参);
上面代码等价于这样
class object {
public:
int f() {
int divisor = 0;
vec.push_back([=](int val){ return val / divisor;});
/// 等价于下面这句
/// vec.push_back([this](int val){ return val / this->divisor;});
/// 默认捕获列表捕获的是this的副本,而不是divisor的副本
}
private:
int divisor = 0;
}
lambda
表达式同样不能捕获static
变量, 在lambda
表达式中使用static
变量只能是按引用。
lambda 表达式与 std::bind 优先选用lambda
auto setSoundB = std::bind(setAlarm, steady_clock::now() + 1h, _1, 30s);
在创建setSoundB这函数对象的时候,通过std::bind
去创建的时候steady_clock::now()
已经进行了计算,而不是在调用时刻进行计算,
同样在具有函数重载的情况时,std::bind
接收的只是函数名称无法参与函数重载的判断,会导致编译不过。