mmxingye

导航

02 | C++lambda容易忽略的点

1.模板化的lambda

  1. 如果我们的 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 表达式有了类似函数模板的强大推导能力。

  1. 如何保证让 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::sortstd::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 表达式注意点
  1. 全局变量和static变量,不用也不需要捕获,直接使用即可
  2. C++14后我们可以使用 初始化捕获 ,即捕获一个表达式,并赋值给一个新变量。
int main(){
  int x=5;
  auto foo=[r=x+1](){return r;};   
}

主要的应用场景有两个:一个是捕获移动后的对象来节省内存,一个是 [tmp=*this] 来确保 this 对象析构了,也可以得到正确的结果。

参考:《现代 C++ 语言核心特性解析》

posted on 2023-04-26 10:15  独立树  阅读(25)  评论(0编辑  收藏  举报