下载网站模板怎么使用教程,网站开发大概多久,成都网站建设顶呱呱,服装类的网站建设Lambda表达式是C11引入的一项重要特性#xff0c;它极大地改变了我们编写匿名函数的方式。
一、为什么会有Lambda表达式
在C11之前#xff0c;当我们需要传递一个简单的函数时#xff0c;通常有以下几种选择#xff1a;
1.1、定义一个单独的函数
// 单独定义的比较函数…Lambda表达式是C11引入的一项重要特性它极大地改变了我们编写匿名函数的方式。
一、为什么会有Lambda表达式
在C11之前当我们需要传递一个简单的函数时通常有以下几种选择
1.1、定义一个单独的函数
// 单独定义的比较函数
bool compareInts(int a, int b) {return a b;
}void functionExample() {std::vectorint numbers {4, 2, 5, 1, 3};// 使用函数指针传递比较逻辑std::sort(numbers.begin(), numbers.end(), compareInts);for (int num : numbers) {std::cout num ;}
}对于只使用一次的逻辑来说过于繁琐无法捕获上下文变量
1.2、使用函数对象(重载operator()的类)
// 函数对象类
class GreaterThan {int threshold;
public:GreaterThan(int t) : threshold(t) {}bool operator()(int value) const {return value threshold;}
};void functorExample() {std::vectorint numbers {1, 3, 5, 7, 9, 2, 4, 6, 8};// 使用函数对象查找第一个大于5的数auto it std::find_if(numbers.begin(), numbers.end(), GreaterThan(5));if (it ! numbers.end()) {std::cout First number greater than 5: *it std::endl;// 输出: First number greater than 5: 7}
}对于简单逻辑显得过于复杂需要额外定义一个类
1.3、使用函数指针
函数指针是指向函数的指针变量它存储的是函数的入口地址。函数指针的类型由函数的返回类型和参数类型共同决定
// 函数声明
ReturnType FunctionName(ParameterType1, ParameterType2, ...);// 对应的函数指针类型
ReturnType (*PointerName)(ParameterType1, ParameterType2, ...);举个例子
// 普通函数
double square(double x) {return x * x;
}// 使用函数指针作为参数的函数
void transformVector(std::vectordouble vec, double (*func)(double)) {for (auto elem : vec) {elem func(elem);}
}void functionPointerExample() {std::vectordouble numbers {1.0, 2.0, 3.0, 4.0};// 传递square函数指针transformVector(numbers, square);for (double num : numbers) {std::cout num ;}std::cout std::endl;
}无法捕获上下文语法较为晦涩不能内联优化可能有性能损失只能指向静态函数不能指向成员函数
方式优点缺点单独函数简单直接可重用污染命名空间无法捕获上下文函数对象可保持状态灵活需要定义额外类代码分散函数指针兼容C可动态选择函数语法复杂无法内联不能捕获上下文
Lambda表达式解决了这些问题提供了一种简洁、内联的方式来定义匿名函数。
二、Lambda表达式基本语法
[capture](parameters) - return_type { // 函数体
}捕获列表(capture)指定lambda表达式如何访问外部变量 参数列表(parameters)与普通函数的参数列表类似 返回类型(return_type)可选的编译器通常可以推导 函数体包含lambda执行的代码
举个例子
auto greet []() { std::cout Hello, World! std::endl;
};
greet(); // 输出: Hello, World!这个lambda没有捕获任何变量没有参数也不返回任何值返回void。
2.1、捕获列表
捕获列表决定了lambda表达式如何访问外部作用域中的变量。捕获方式有多种
2.1.1、值捕获
int x 10;
auto lambda [x]() { std::cout x std::endl;
};
x 20;
lambda(); // 输出: 10值捕获创建了变量的副本lambda内部使用的是捕获时的值。
2.1.2、引用捕获
int x 10;
auto lambda [x]() { std::cout x std::endl;
};
x 20;
lambda(); // 输出: 20引用捕获使用变量的引用lambda内部访问的是变量的当前值。
2.1.3、隐式捕获
int x 10;
int y 20;
auto lambda1 []() { std::cout x , y std::endl; }; // 值捕获所有
auto lambda2 []() { std::cout x , y std::endl; }; // 引用捕获所有[]表示值捕获所有外部变量[]表示引用捕获所有外部变量。
2.1.5、this指针捕获
class MyClass {int value 42;
public:void print() {auto lambda [this]() { std::cout value std::endl; };lambda();}
};2.1.4、混合捕获
int x 10;
int y 20;
auto lambda [, y]() { // 值捕获x引用捕获ystd::cout x , y std::endl;
};在类的成员函数中lambda可以捕获this指针来访问类的成员
2.2、参数列表
Lambda表达式的参数列表与普通函数类似
auto add [](int a, int b) { return a b;
};
std::cout add(5, 3) std::endl; // 输出: 82.3、返回类型
返回类型通常可以省略编译器会自动推导
auto square [](int x) { return x * x; }; // 返回类型推导为int当函数体包含多个return语句且类型不同或者返回类型不明显时需要显式指定
auto conditional [](int x) - double {if (x 0) return 1.0;else return 0.0;
};2.4、可变Lambda (mutable)
默认情况下值捕获的变量在lambda内是const的。使用mutable关键字可以修改这些副本
int x 0;
auto counter [x]() mutable {x; // 没有mutable会编译错误std::cout x std::endl;
};
counter(); // 输出: 1
counter(); // 输出: 2
std::cout x std::endl; // 输出: 0 (原始x未被修改)2.5、Lambda表达式的类型
每个lambda表达式都有一个唯一的、未命名的类型。要存储lambda通常使用auto或std::function
auto lambda []() { /* ... */ };
std::functionvoid() lambda []() { /* ... */ };三、用法
3.1、作为函数参数
Lambda常用于STL算法中作为谓词
std::vectorint numbers {1, 2, 3, 4, 5};
std::for_each(numbers.begin(), numbers.end(), [](int n) {std::cout n ;
});3.2、立即调用
auto make_multiplier [](int factor) {return [factor](int x) { return x * factor; };
};auto times3 make_multiplier(3);
std::cout times3(5) std::endl; // 输出: 153.3、泛型Lambda (C14)
C14引入了泛型lambda允许使用auto参数
auto print [](const auto value) {std::cout value std::endl;
};print(42); // int
print(3.14); // double
print(Hello); // const char*3.4、捕获表达式 (C14)
C14允许在捕获列表中初始化变量
int x 10;
auto lambda [y x 5]() {std::cout y std::endl; // 输出: 15
};3.5、constexpr Lambda (C17)
C17允许lambda在编译时求值
constexpr auto square [](int x) { return x * x; };
static_assert(square(5) 25);3.6、举个例子
std::vectorstd::pairint, std::string items {{3, three}, {1, one}, {4, four}, {2, two}
};// 按第一个元素升序排序
std::sort(items.begin(), items.end(), [](const auto a, const auto b) { return a.first b.first; });// 按第二个元素长度降序排序
std::sort(items.begin(), items.end(), [](const auto a, const auto b) { return a.second.size() b.second.size(); });四、Lambda存在的限制
不能有默认参数lambda表达式不支持默认参数不能递归调用自身除非通过std::function或捕获自身引用不能是虚函数lambda表达式不能是虚函数
五、考点
5.1、为什么直接使用auto存储lambda通常比std::function更高效
std::function使用了类型擦除(Type Erasure)技术它可以存储任何可调用对象函数指针、成员函数指针、函数对象、Lambda等这种通用性带来了运行时开销
5.2、Lambda表达式在编译器内部是如何实现的
编译器会为每个Lambda生成一个唯一的匿名类其中
捕获的变量成为该类的成员变量operator()被重载为Lambda的函数体根据捕获方式决定成员变量是值还是引用
int x 10;
auto lambda [x](int y) { return x y; };// 编译后class __AnonymousLambda {int x;
public:__AnonymousLambda(int x) : x(x) {}int operator()(int y) const { return x y; }
};5.3、什么是悬空引用
std::functionvoid() createLambda() {int x 10;return [x]() { std::cout x; }; // x已销毁
}返回的lambda表达式无法再次获得x。
5.4、怎么递归Lambda表达式
由于Lambda没有名称无法直接递归调用自己。可以通过以下方式实现
std::functionint(int) factorial [factorial](int n) {return n 1 ? 1 : n * factorial(n - 1);
};5.5、解释以下捕获方式的区别[], [], [], [this]
[]不捕获任何外部变量[]值捕获所有外部变量创建副本[]引用捕获所有外部变量使用引用[this]捕获当前类的this指针可以访问成员变量和函数
5.6、 Lambda表达式与普通函数有什么区别
匿名性Lambda是匿名函数无需命名捕获能力可以捕获上下文中的变量内联定义可以在使用的地方直接定义类型每个Lambda有唯一的、编译器生成的类型灵活性可以更方便地作为参数传递