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

这篇具有很好参考价值的文章主要介绍了【C++】c++11新特性(二)--Lambda函数及function(包装器)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

Lambda函数

基本概念

基本语法

lambda 捕获(capture)

1. 不捕获任何变量

2. 按值捕获

3. 按引用捕获

4. 混合捕获

5. 捕获this 指针

包装器 function

基本概念

使用场景

1. 给function对象赋值

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

  3. 存储在容器中

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


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异常文章来源地址https://www.toymoban.com/news/detail-859714.html


到了这里,关于【C++】c++11新特性(二)--Lambda函数及function(包装器)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++杂货铺】C++11新特性——lambda

    在 C++98 中,如果要对一个数据集合中的元素进行排序,可以使用 std::sort 方法,下面代码是对一个整型集合进行排序。 小Tips :上面的 greater 是一个仿函数,这里传递仿函数是用来控制大小比较的。关于仿函数,在前面的文章中已经多次使用,在【C++杂货铺】优先级队列的使

    2024年02月03日
    浏览(37)
  • 【C++11】lambda表达式 包装器

    在C++98中,如果想要对一个数据集合中的元素进行排序,可以使用std::sort方法: 如果待排序元素为自定义类型,需要用户定义排序时的比较规则: 如果仿函数命名比较规范的话,像上面的命名方式的话那还好,如果遇到了像cmp1 cmp2 cmp3…这种命名方式而且还没有注释的话可以

    2024年02月12日
    浏览(43)
  • 【C++】C++11右值引用|新增默认成员函数|可变参数模版|lambda表达式

    在C++11之前,我们只有引用的概念,没有接触到所谓的左值引用或者是右值引用这种概念,从C++11开始,增加了右值引用的概念,那么现在我们将对引用进行一个概念上的区分。在此之前我们所说的引用都是左值引用,对于左值引用相关的内容,可以去看一看博主之前写的文章

    2024年02月15日
    浏览(58)
  • C++11新特性lambda 表达式

    Lambda 表达式的基本语法是:[] (参数列表) - 返回值类型 {函数体}。 方括号([])表示捕获列表,用来指定在 lambda 表达式中可以访问的外部变量。 参数列表和返回值类型与普通函数的参数列表和返回值类型相同。 函数体则是实际的代码逻辑。 不接受任何参数:[] { 函数体 } 接受

    2024年02月14日
    浏览(39)
  • C11新特性之Lambda表达式

    优点:  1.可以定义简短的函数。 2.使用lambda表达式使代码更紧凑,可读性更好。 语法: [] 表示不捕获任何变量 [this] 表示值传递方式捕捉当前的 this 指针  [] 表示引用传递方式捕捉所有父作用域的变量(包括 this ) [var] 表示 引用传递 捕捉变量 var [=] 表示值传递方式捕获所

    2023年04月22日
    浏览(43)
  • java学习路程之篇三、进阶知识、面向对象高级、接口新特性、代码块、内部类、Lambda表达式、窗体、组件、事件

    概述 生成值类型响应式数据 通过 .value 值修改 生成对象和数组类型的响应式对象选用 reactive 方式比较好 html JavaScript 概述 reactive 方法根据传入的对象,创建返回一个深度响应式对象。响应式对象看起来和传入的对象一样。但是,响应式对象属性值改动,不管层级有多深,都

    2024年02月16日
    浏览(38)
  • Java8新特性1——函数式接口&lambda表达式

    注:以下内容基于Java 8,所有代码都已在Java 8环境下测试通过 目录: Java8新特性1——函数式接口lambda表达式 Java8新特性2——方法引用 Java8新特性3——Stream 如果在一个接口中, 有且只有一个抽象方法 ,则该接口被称为函数式接口。如: 注: 可以在接口前使用 @FunctionalInt

    2024年02月10日
    浏览(44)
  • JDK8中的新特性(Lambda、函数式接口、方法引用、Stream)

    1.1 关于Java8新特性简介 Java 8 (又称为 JDK 8或JDK1.8) 是 Java 语言开发的一个主要版本。 Java 8 是oracle公司于2014年3月发布,可以看成是自Java 5 以来 最具革命性 的版本。Java 8为Java语言、编译器、类库、开发工具与JVM带来了大量新特性。 特性: 速度更快 代码更少(增加了新的语法

    2024年02月05日
    浏览(45)
  • C++ 11 Lambda表达式

    https://www.cnblogs.com/DswCnblog/p/5629165.html C++11的一大亮点就是引入了Lambda表达式。利用Lambda表达式,可以方便的定义和创建匿名函数。对于C++这门语言来说来说,“Lambda表达式”或“匿名函数”这些概念听起来好像很深奥,但很多高级语言在很早以前就已经提供了Lambda表达式的功

    2024年02月10日
    浏览(50)
  • 【C++】C++11——lambda表达式

    我们之前都是通过函数指针、仿函数的方式可以像函数使用的对象,在C++11之后,就有了Lambda表达式 为了实现一个比较算法, 都要重新去写一个类,如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,看代码的人就遭殃了,非常的烦,这些都非常地不方便

    2024年01月17日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包