【C++】c++11新特性(二)–Lambda函数及function(包装器)

目录


Lambda函数

基本概念

       lambda 函数(也被称为匿名函数或lambda表达式)是一种简洁的方式,用于定义在代码块中使用的临时函数对象。

       lambda 函数特别适用于需要小函数但又不希望定义完整函数或函数对象的情况。通常与 STL 算法结合使用,为算法提供自定义的操作。

基本语法

lambda 函数的基本语法如下:

[Capture-list]  (Parameters)  Mutable -> Return-Type  { function-body }

(Capture list) — 捕获列表

        用于捕获外部作用域的变量,使得这些变量可以在lambda函数体内部被访问。捕获列表可以是以值捕获([] 或 [=]);也可以是按引用[&] 或 [&variable])。

  (Parameters) — 参数列表

        与普通函数的参数列表类似,用于指定lambda函数的输入参数。

  (Return-Type) — 返回类型

         可以显式指定lambda函数的返回类型。

  (function-body) — 函数体

        包含lambda函数的实际代码逻辑;在该函数体内,除了可以使用其参数外,还可以使用所有捕获 到的变量。

注意:

1. 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来 判断接下来的代码      是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda 函数使用。因此,该部      分不能省略

2. 参数列表。如果不需要参数传递,则可以连同()一起省略

3. mutable。默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使      用该修饰符时,参数列表不可省略(即使参数为空)。

4. 返回类型。没有返回值时此部分可省略返回值类型明确情况下,也可省略,由编译器对      返回类型进行推导。

总结:参数列表返回类型在lambda表达式中是可选的捕获列表函数体必需的

lambda 捕获(capture)

1. 不捕获任何变量

当lambda表达式不需要访问任何外部变量时,可以使用空的捕获子句。

最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情


int main() 
{  
    // 最简单的lambda表达式, 该lambda表达式没有任何意义
    auto lambda = []() {  
        cout << "This lambda captures nothing." << endl;  
    };  

    lambda(); // 输出: This lambda captures nothing. 
 
    return 0;  
}

2. 按值捕获

       捕获变量按值意味着在lambda创建时,捕获的变量会被复制一份到lambda中,lambda内部使用的是这份复制的变量,因此原始变量的后续修改不会影响lambda内部的变量。


//按值捕获 特定变量
int test_val() 
{
    int x = 10;
    int y = 15;

    auto lambda = [x]() {
        cout << "Value of x inside lambda: " << x << endl;  //输出 10
    };

    x = 20; // 修改外部的x,不会影响lambda内部的x  
    lambda(); // 输出: Value of x inside lambda: 10  

    return 0;
}

//按值捕获 所有变量
int test_all() 
{
    int x = 10;
    int y = 15;

    auto lambda = [=]() {
        cout << "Value of x and y inside lambda: " << x + y << endl; //输出 25
    };

    x = 20; // 修改外部的x,不会影响lambda内部的x  
    lambda(); // 输出: Value of x inside lambda: 25  

    return 0;
}

3. 按引用捕获

       捕获变量按引用意味着lambda函数体内部直接使用原始变量的引用,因此原始变量的任何修改都会在lambda内部反映出来。

//按引用捕获 特定变量
int test_val() 
{
    int x = 10;
    int y = 15;  // y没有被捕获

    auto lambda = [&x]() {
        cout << "Value of x inside lambda: " << x << endl; 
    };

    x = 20; // 修改外部的x,会影响到lambda内部的x  
    lambda(); // 输出: Value of x inside lambda: 20  

    return 0;
}

//按引用捕获 所有变量
int test_all() 
{
    int x = 10;
    int y = 15;

    auto lambda = [&]() {
        cout << "Value of x and y inside lambda: " << x << " " << y << endl; 
    };

    x = 20; // 修改外部的x,会影响lambda内部的x 
    y = 30; // 修改外部的y,会影响lambda内部的y 
    lambda(); // 输出: Value of x and y inside lambda: 20 and 30 

    return 0;
}

4. 混合捕获

可以同时按值按引用捕获不同的变量。

int main()
{
    int x = 10;
    int y = 15;

    // 按值捕获x,按引用捕获y
    auto lambda = [x, &y]() {
        cout << "Value of x and y inside lambda: " << x << " " << y << endl; 
    };

    x = 20;   // 修改外部的 x,不会影响lambda内部的 x 
    y = 30;   // 修改外部的 y,会影响lambda内部的 y

    lambda(); // 输出: Value of x inside lambda: 10  30  

    return 0;
}

5. 捕获this 指针

在类的成员函数中,可以通过捕获this指针来访问类的成员变量和成员函数

class A {
public:
    A() : a(0) {}

    void func1()
    {
        int b = 4; // 捕获b
        auto lambda = [b]() {
            cout << "捕获b:" << b;
            return; 
        };

        lambda();  // 输出 lambda 内部的 b : 4
    }

    void func2()
    {
        int b = 4; // b没有被捕获
        // 传 this 指针
        auto lambda = [this]() {
            cout << "捕获a:" << a;
            return; 
        };

        lambda();  // 输出 lambda 内部的 a : 0
    }

private:
    int a;
};

int main() 
{
    A obj;
    obj.func1();
    obj.func2();
    
    return 0;
}

如果把上面类A的成员函数 func1中 按值捕获的 b 变量 在 lambda表达式中进行修改,即func1函数变成如下形式:

void func1()
    {
        int b = 4; // 捕获b
        auto lambda = [b]() {

            b += 6;
            cout << “捕获b:” << b;
            return; 
        };

        lambda();  // 输出 lambda 内部的 b : 4
    }

此时,编译器会报错:“b”: 无法在非可变 lambda 中修改通过复制捕获

因为在默认情况下,lambda函数中有一个 operator()操作符,其默认为const,即lambda函数总是一个const函数;捕获列表中的变量a,直接成为lambda函数成员,且由其构造函数在初始化列表中直接初始化。

因此,lambda表达式不可以修改按值捕获的变量,因为operator() 被编译器默认扩展为const属性

如果像强制修改,可将关键字mutable应用lambda表达式,这样会使编译器扩展的operator()为不带const属性的接口,便可以修该被捕获的变量!

以上func1函数体中的 lambda 表达式 修改成如下形式:

auto lambda = [b] () mutable {

            b += 6;       //ok
            cout << “捕获b:” << b;
            return; 
        };

包装器 function

基本概念

function是一个通用的、类型安全的函数对象包装器,它允许将任何可调用的目标(如函数、lambda表达式、函数对象或其他function对象)赋值给它,并可以像调用普通函数一样调用它。function通常与lambda表达式一起使用,以实现更灵活和可重用的代码。

说明:使用function需要包含 <functional>头文件,然后定义function对象,并指定其调用签名。调用签名定义了该函数对象接受的参数类型和返回类型

#include <iostream>  
#include <functional> 
using namespace std; 
  
int add(int a, int b) 
{  
    return a + b;  
}  
  
int main() 
{  
    // 定义一个function对象,接受两个int参数并返回一个int  
    function<int(int, int)> func;  
  
    // ... 后续可以将func与任何匹配签名的可调用对象关联起来  
    return 0;  
}

使用场景

1. 给function对象赋值

可以将 普通函数、lambda表达式、函数对象或其他function对象赋值给function

int add(int x, int y)
{
    return x + y;
}

int main() 
{
    function<int(int, int)> func;

    // 赋值一个普通函数  
    func = add;
    cout << func(2, 4) << endl; // 输出6

    // 赋值一个lambda表达式  
    func = [](int x, int y) { return x * y; };
    cout << func(2, 5) << endl; // 输出7  

    // 赋值另一个function对象(如果它们的签名兼容)  
    function<int(int, int)> anotherFunc = add;
    func = anotherFunc;
    cout << func(2, 6) << endl; // 输出8 

    return 0;
}

 2. 作为函数参数和返回值

function对象可以作为函数参数传递,也可以作为函数的返回值。这使得编写更加通用和灵活的代码。

int add(int x, int y)
{
    return x + y;
}

// 作为函数参数传递
void process(function<int(int, int)> ft, int a, int b) 
{
    int result = ft(a, b);
    cout << "Result: " << result << endl;
}

// 作为函数返回值
function<int(int, int)> funcOperation()
{
    return [](int a, int b) { return a - b; };
}

int main() 
{
    process(add, 2, 3);          
    process([](int a, int b) { return a * b; }, 2, 3); 
    process(funcOperation(), 5, 3); 

    return 0;
}

  3. 存储在容器中

function对象可以存储在标准库容器中,如vectormap,使得可以动态地管理一组函数对象。

int main() 
{
    vector<function<int(int)>> funcs;

    funcs.push_back([](int x) { return x * x; });
    funcs.push_back([](int x) { return x * x * x; });

    for (const auto& e : funcs) 
    {
        cout << e(2) << endl;   // 输出4和8  
    }

    return 0;
}

4. 绑定成员函数和带参数的函数

如果想要绑定类的成员函数或者带参数的函数,需要使用bind或者lambda表达式来捕获所需的上下文。

class Plus
{
public:
    static int plusi(int a, int b)
    {
        return a + b;
    }

    int plusd(int a, int b)
    {
        return a + b;
    }
  
};

int main() 
{
    Plus obj;

    // 类的静态成员函数
    function<int(int, int)> func4 = &Plus::plusi;
    cout << func4(1, 2) << endl;

    //类的非静态成员函数
    function<double(Plus, double, double)> func5 = &Plus::plusd;
    cout << func5(Plus(), 1, 2) << endl;

    // 使用bind绑定成员函数  
    function<int(int, int)> memberFunc = bind(&Plus::plusd, &obj, placeholders::_1, placeholders::_2);
    cout << memberFunc(3, 4) << endl; // 输出: 7

    // 使用lambda表达式捕获对象指针并调用成员函数  
    function<int(int, int)> lambdaMemberFunc = [&obj](int x, int y) { return obj.plusd(x, y); };
    cout << lambdaMemberFunc(4, 5) << endl; // 输出: 9  

    return 0;
}

说明:

1. function可以绑定到任何可调用的目标,包括成员函数和带有绑定参数的函数。这需要使用 bind 或 lambda表达式 来捕获所需的上下文

2. function是空类型安全的,这意味着它会在运行时检查所赋值的可调用对象是否与它的签名兼容。如果不兼容,将会抛出std::bad_function_call异常

版权声明:本文为博主作者:HZ_ENG原创文章,版权归属原作者,如果侵权,请联系我们删除!

原文链接:https://blog.csdn.net/HZ_ENG/article/details/137399763

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2024年4月16日
下一篇 2024年4月16日

相关推荐