C++ lambda的几个坑

  1. 捕获列表的生命周期问题:
#include <iostream>
#include <functional>

using namespace std;

function<int()> make_lambda() {
    int x = 42;

    function<int()> lambda = [x]() -> int {
        return x;
    };

    return lambda;
}

int main() {
    function<int()> lambda = make_lambda();
    cout << lambda() << endl; // 输出结果为42

    // 直接释放x,此时lambda中存储了x的引用
    int x = 0;
    lambda = [x]() -> int {
        return x;
    };

    cout << lambda() << endl; // 输出结果为随机值(悬垂指针)
    return 0;
}

在上述代码中,make_lambda()函数返回一个使用值捕获的lambda表达式,其中捕获了一个整数变量x。但是当lambda表达式被存储之后,x所在的栈帧就已经被销毁了,此时如果直接释放x,则lambda表达式中存储的是一个指向无效内存地址的引用,会导致悬垂指针的问题。

  1. mutable关键字的使用:
#include <iostream>
#include <functional>

using namespace std;

void modify_lambda() {
    int x = 42;

    function<int()> lambda = [=]() mutable -> int {
        x++;
        return x;
    };

    cout << lambda() << endl; // 输出结果为43
    cout << lambda() << endl; // 输出结果为44
}

int main() {
    modify_lambda();

    int x = 42;

    function<int()> lambda = [&x]() mutable -> int {
        x++;
        return x;
    };

    cout << lambda() << endl; // 输出结果为43
    cout << x << endl; // 输出结果为42
    return 0;
}

在上述代码中,modify_lambda()函数演示了在使用值捕获的lambda表达式中添加mutable关键字,并尝试修改被捕获的变量x。注意,由于使用了mutable,因此x的值可以被修改,结果为输出44而不是42。但是这也意味着整个程序中存在两个同名的变量x,因此在使用引用捕获的lambda表达式中修改x的值时,实际上并没有修改到外部的变量x,导致程序的行为与预期不一致。

  1. 参数类型的推导:
#include <iostream>
#include <functional>

using namespace std;

void print_lambda(function<void(int)> lambda) {
    lambda(10);
}

int main() {
    // 下面这行代码会导致编译错误
    // 因为编译器无法确定lambda表达式中使用的参数类型
    // print_lambda([](x) { cout << x << endl; });

    // 应该利用auto关键字或显式指定参数类型
    print_lambda([](int x) { cout << x << endl; });
    // 或者
    auto lambda = [](int x) { cout << x << endl; };
    print_lambda(lambda);

    return 0;
}

在上述代码中,定义了一个函数print_lambda(),它接受一个使用int类型参数的lambda表达式,并输出参数的值。但是如果在调用函数时,直接使用未指定类型的参数名x,则编译器无法确定参数的类型,导致编译错误。因此,在使用lambda表达式时,应该显式指定参数类型或使用auto关键字进行自动推导。

  1. 返回值类型的推导:
#include <iostream>
#include <functional>

using namespace std;

function<bool(int)> make_lambda() {
    function<bool(int)> lambda = [](int x) {
        if (x > 0) {
            return true;
        }
        else {
            return string("error"); // 编译器报错:转换类型不匹配
        }
    };

    return lambda;
}

int main() {
    auto lambda = make_lambda();
    cout << lambda(10) << endl; // 输出结果为true

    return 0;
}

在上述代码中,函数make_lambda()返回一个使用自动推导返回值类型的lambda表达式,并尝试返回两种不同的类型。但是由于编译器无法确定其返回值类型,会导致类型转换错误的编译错误。因此,如果lambda表达式中存在多个可能的返回类型,应该显式指定返回值类型以避免出现错误。

  1. 捕获列表中变量的初始化:
#include <iostream>
#include <functional>

using namespace std;

void print_lambda(function<void()> lambda) {
    lambda();
}

int main() {
    int x = 42;

    // 初始化一个捕获列表中的变量
    auto lambda = [x = x]() mutable {
        cout << x << endl;
        x++;
    };

    print_lambda(lambda); // 输出结果为42
    print_lambda(lambda); // 输出结果为42

    return 0;
}

在上述代码中,lambda表达式使用了初始化语法来初始化捕获列表中的变量x。但是由于每次调用lambda表达式时都会生成一个新的实例,因此如果直接在捕获列表中初始化x,则每个实例都会使用相同的初始值,导致其他lambda表达式的调用结果无法正确反映出更改后的变量值。因此,为了避免这种问题,应该将初始化移到lambda表达式的函数体内部,以便每次调用都能得到正确的值。

posted @ 2023-04-04 15:42  flxx  阅读(466)  评论(0编辑  收藏  举报