Lambda表达式

什么是Lambda表达式

Lambda表达式是一种匿名函数,它可以被用作变量或参数传递给其他函数。在C++中Lambda表达式是一个重载了括号运行符的类。

Lambda语法规则

[caputure](params) opt->return { 函数体 };

caputure: 捕获方式
params: 传参,可以多个
opt: mutable(值传递时可以修改外部变量), exception(是否抛出异常以及何种异常), attribute(属性)
Lambda传参方式有两种,一种捕获,一种类似函数传参
捕获:
	要求Lambda函数内部使用变量必须在定义Lambda之前存在
	捕获有不捕获[],值捕获[=],引用捕获[&]
捕获扩展:
    [=, &mem]: 表示除了mem变量采用引用捕获,其他都是值捕获
    [mem]: 只捕获mem变量
    [=]: 值捕获外部作用域中的变量(要求是在声明Lambda之前的变量)
传参:要求则没有那么高,参数只需在调用之前存在即可,传参类型可以值也可以是引用指针等

Lambda示例

{
	// 无参值捕获
	int x = 0, y = 0;
	auto add = [=]() mutable {
		// 此处修改x, y的值不会导致外界的x,y修改。如果不加mutable,x和y将是const int类型,无法被修改
		// 此处x和y是一份拷贝
		x = 10;
		y = 10;
		return x + y;
	};
	std::cout << add() << ", " << x << ", " << y << std::endl;
}

{
	// 两者不矛盾,中括号里的是捕获外界变量并以值传递方式进入函数内部,而传参是以引用方式,故可以修改值
	// 中括号的作用是在没有参数的时候可以在lambda函数内部使用外部变量

	// 有参值捕获
	auto add = [=](int &x, int &y) mutable {
		x = 10;
		y = 10;
		return x + y;
	};
	int x = 0, y = 0;
	std::cout << add(x, y) << std::endl; // 调用完后x,y的值会发生修改
}

{
	// 引用传参
    int a = 0;
    auto add = [&](int &x, int &y) mutable->int {
        a = 11;
        x = 11;
        y = 11;
        return a + x + y; 
    };
    int x = 2, y = 3;
    std::cout << add(x, y) << std::endl;

    // a = 11, x = 11, y = 11
    std::cout << "a = " << a << ", x = " << x << ", y = " << y << std::endl;
}

C++17对lambda的扩展

C++11中只能捕获this(this是当前对象的一个只读的引用),而无法捕获*this。
C++17对其进行了扩展,使其可以捕获*this,*this是当前对象的一个拷贝,捕获当前对象的拷贝。能够确保当前对象释放后, lambda表达式能安全的调用this中的变量和方法。
class Any {
public:
    Any() : this_e(this) { printf("%s() this = %p\n", __func__, this); }
    Any(const Any &other) { printf("%s(const Any &other)\n", __func__); }
    ~Any() { printf("%s()\n", __func__); }

    void print() const { printf("%s() this = %p\n", __func__, this); }
    void print_nonconst() { printf("%s()\n", __func__); }

    auto exec() -> std::function<void()>
    {
        auto lambda = [*this]()
        {
            // 此时*this是const Any类型, 会调用一次拷贝构造, 当不存在拷贝构造时无法捕获*this
            printf("%s()\n", __func__);
            assert(this != this_e); // 由于拷贝构造不会修改this_e的值,故this_e还保留着之前对象的地址
            this->print(); // 同样可以使用this(但只能调用const属性的函数), 此时的this是*this新创建的Any的地址

            // 通过const_cast来消除const,从而调用非const属性的函数
            const_cast<Any *>(this)->print_nonconst();
        };

        return lambda;
    }

    void *this_e;
};

auto get() -> std::function<void()>
{
    Any any;
    return any.exec();
}

int main(int argc, char **argv)
{
    get()();
    return 0;
}

使用c++11编译时会弹出警告,‘*this’ capture only available with -std=c++1z or -std=gnu++1z,使用c++17则不会。
虽然上述代码不会在c++11环境下崩溃,但是并非说明其就是安全的。

你可能感兴趣的:(c++)