02 | C++lambda容易忽略的点
1.模板化的lambda
- 如果我们的 lambda 接收容器(假定我们老老实实的传入容器),并且要提取出容器中的元素类型,应该怎么做呢?
- 之前我们可能这样做
auto f=[](auto vector){
using T=typename decltype(vector)::value_type;
///......
}
首先说这样做并不明显我们要传入的参数是一个 vector 的容器,其次在实现上我们假定 vector 的内部有 value_type
这也是不合理的。
- 现在我们可以这样(C++20标准)
auto f = []<typename T>(std::vector<T> vector){
//....
}
这样就让 lambda
表达式有了类似函数模板的强大推导能力。
- 如何保证让
lambda
接收的参数就是一个vector
容器呢?
- 之前我们可能这样做
template <typename T>struct is_std_vector : std::false_type{};
template <typename T>struct is_std_vector<std::vector<T>> : std::true_type{};
auto f=[](auto vector){
static_assert(is_std_vector<decltype(vector)>::value,"");
}
- 现在 C++20 的标准也让我们省去了写
is_xxx
这种形式的类模板
2.可赋值的无状态 lambda 表达式
在C++20之前,无状态的 lambda 表达式既不能构造也无法赋值,当我们使用 std::sort
和 std::find_if
这类的函数时, lambda
是以函数的对象的身份通过函数参数传递进去的。可是像 map
的构造函数需要 lambda
的模板参数,相当于以函数类型的身份传递进去,奈何 map
类明明知道了该 lambda
表达式的类型却无法创建一个实例,因此我们可能写出这样的错误代码
auto greater=[](auto x,auto y){return x>y;}
std::map<std::string,int,decltype(greater)> mymap;
和
auto greater=[](auto x,auto y){return x>y;}
std::map<std::string,int,decltype(greater)> mymap1,mymap2;
mymap1=mymap2;
但如果我们使用了C++20标准的编译器则不会报错,甚至比函数重载还要简单。
class Person{
public:
string name;
int age;
Person(string n, int a){
name = n;
age = a;
}
// bool operator<(const Person &p) const //注意这里的两个const
// {
// return (age < p.age) || (age == p.age && name.length() < p.name.length()) ;
// }
};
bool MyCompare(const Person &p1, const Person &p2) {//普通的函数
return (p1.age < p2.age) || (p1.age == p2.age && p1.name.length() < p2.name.length());
}
auto MyCom=[](const Person &p1, const Person &p2) {//普通的函数
return (p1.age < p2.age) || (p1.age == p2.age && p1.name.length() < p2.name.length());
};
int main(){
// map<Person,int,decltype(&MyCompare)> group(MyCompare);
map<Person,int,decltype(MyCom)> group;
group[Person("Mark", 17)] = 40561;
group[Person("Andrew",18)] = 40562;
for (auto ii = group.begin() ; ii != group.end() ; ii++)
cout << ii->first.name
<< " " << ii->first.age
<< " : " << ii->second
<< endl;
}
3.[=,this] 形式的lambda也不算错误
在 C++20,之前我们写 [=]
就默认包含了 this
指针.但是为了和 [=,*this]
以示区分,引入了 [=,this]
。
形式 | 捕获区分 |
---|---|
[=] 或者 [=,this] |
捕获了 this 指针,不过如果使用 后一种写法要用 C++20 标准 |
[=,*this] |
捕获了对象的副本 |
- 其他一些 lambda 表达式注意点
- 全局变量和static变量,不用也不需要捕获,直接使用即可
- C++14后我们可以使用
初始化捕获
,即捕获一个表达式,并赋值给一个新变量。int main(){ int x=5; auto foo=[r=x+1](){return r;}; }
主要的应用场景有两个:一个是捕获移动后的对象来节省内存,一个是
[tmp=*this]
来确保 this 对象析构了,也可以得到正确的结果。
参考:《现代 C++ 语言核心特性解析》