当前位置: 首页 > news >正文

C ++匿名函数:揭开C++ Lambda表达式的神秘面纱

潜意识编程:揭秘C++ Lambda表达式的神秘面纱 Subconscious Programming: Unveiling the Mystery of C++ Lambda Expressions

  • 引言:Lambda表达式的魅力 (The Charm of C++ Lambda Expressions)
  • Lambda表达式简介与基本概念 (Introduction and Basic Concepts of Lambda Expressions)
    • a. 匿名函数 (Anonymous Functions)
    • b. 闭包 (Closures)
    • c. 自动类型推导 (Automatic Type Deduction)
  • C++中的Lambda表达式详解 (In-depth Explanation of C++ Lambda Expressions)
    • a. 语法结构 (Syntax Structure)
    • b.Lambda表达式原型
    • c. 捕获方式 (Capture Modes)
    • d.捕获示例
    • e. 使用场景与优势 (Use Cases and Advantages)
  • 实际应用案例 (Practical Examples)
    • a.使用Lambda表达式简化算法 (Simplifying Algorithms with Lambda Expressions)
    • b. 在容器操作中使用Lambda (Using Lambda in Container Operations)
    • c. 异步编程与Lambda (Asynchronous Programming and Lambda)
  • Lambda表达式的高级用法 (Advanced Usage of Lambda Expressions)
    • a. Lambda表达式中的条件表达式 (Conditional Expressions in Lambda)
    • b. 嵌套Lambda表达式 (Nested Lambda Expressions)
    • c. 使用Lambda表达式实现惰性求值 (Lazy Evaluation with Lambda Expressions)
  • Qt中的lambda运用
    • qt创建线程使用lambda表达式
    • connect使用lambda表达式
    • 异步操作使用lambda表达式
  • Lambda表达式的性能与效率 (Performance and Efficiency of Lambda Expressions)
    • a. 编译器优化 (Compiler Optimizations)
    • b. 常见陷阱与注意事项 (Common Pitfalls and Precautions)
  • 常见问题与答疑 (Frequently Asked Questions)
    • a. 如何将Lambda表达式作为函数参数传递? (How to pass a Lambda expression as a function parameter?)
    • b. Lambda表达式如何访问类成员变量? (How can a Lambda expression access class member variables?)
    • c. 如何在Lambda表达式中使用`nothrow`? (How to use `nothrow` in Lambda expressions?)
    • d. 如何创建可变Lambda表达式? (How to create a mutable Lambda expression?)
    • e. 如何将Lambda表达式转换为函数指针? (How to convert a Lambda expression to a function pointer?)
    • f. Lambda表达式能否捕获全局变量? (Can a Lambda expression capture global variables?)
  • C++标准的发展与Lambda表达式 (Evolution of C++ Standards and Lambda Expressions)
    • a.C++11中的Lambda (Lambda in C++11)
    • b. C++14中的Lambda扩展 (Lambda Extensions in C++14)
    • c. C++17与C++20中的新增功能 (New Features in C++17 and C++20)
  • 从编译器底层和内存角度看Lambda表达式及其与其他函数的区别(Lambda expressions and their differences from other functions from the Perspective of Compiler bottom layer and memory)
    • a. Lambda表达式的底层实现 (Underlying Implementation of Lambda Expressions)
    • b. 内存使用 (Memory Usage)
    • c. 与其他函数的区别 (Difference from Other Functions)
  • 结语

引言:Lambda表达式的魅力 (The Charm of C++ Lambda Expressions)

在编程世界里,每一门编程语言都有自己的独特之处。C++,作为一门混合了过程式、面向对象和泛型编程的高效语言,一直以来都吸引着无数程序员。

而C++11标准引入的Lambda表达式更是为C++注入了新鲜血液。Lambda表达式简洁、易于理解,让我们在编程时能够更高效地表达自己的思想。本文将深入探讨C++ Lambda表达式的基本概念、语法、应用场景以及性能方面的知识。通过这篇文章,您将了解到Lambda表达式的强大功能,并学会如何在实际项目中灵活运用,提升编程效率和代码质量。现在,就让我们一起探索C++ Lambda表达式的魅力之旅吧!

Lambda表达式简介与基本概念 (Introduction and Basic Concepts of Lambda Expressions)

a. 匿名函数 (Anonymous Functions)

Lambda表达式来源于数学中的λ演算,是一种可以定义在任何地方且没有名称的函数。在C++中,Lambda表达式作为一种轻量级的匿名函数实现,可以实现函数式编程范式,让我们能够编写更简洁、灵活的代码。与普通函数相比,Lambda表达式具有更高的局部性和自包含性,因为它可以直接捕获其所在作用域的变量,这使得Lambda表达式非常适合用于定义简短的一次性函数,尤其是在使用算法库时。

b. 闭包 (Closures)

闭包是指一个函数与其引用环境的组合。在C++中,Lambda表达式可以捕获所在作用域中的局部变量,形成闭包。闭包可以存储捕获变量的状态,即使Lambda表达式的执行上下文已经消失,闭包仍然保持有效。这使得Lambda表达式在处理异步编程、事件驱动编程等场景时具有优势。

c. 自动类型推导 (Automatic Type Deduction)

C++11中引入的自动类型推导功能允许编译器在编译期间推断出一个表达式的类型,而无需显式地声明。Lambda表达式可以利用自动类型推导功能自动推断出其参数类型、返回类型,使得代码更加简洁易读。例如,我们可以使用auto关键字直接定义一个Lambda表达式,而无需声明具体类型。这大大提高了编程效率和代码的可维护性。

C++中的Lambda表达式详解 (In-depth Explanation of C++ Lambda Expressions)

a. 语法结构 (Syntax Structure)

Lambda表达式的基本语法结构包括捕获列表、参数列表、可选的返回类型以及函数体。捕获列表定义了哪些外部变量可以被Lambda表达式访问,参数列表定义了Lambda表达式的输入参数。以下是一个Lambda表达式的示例:

auto example_lambda = [](int x, int y) -> int { return x + y; };

b.Lambda表达式原型

//(1)
[ captures ] ( params ) specs requires(optional) { body }   
//(2)  (until C++23) 
[ captures ] specs { body }  
//(3)  (since C++20)
[ captures ] < tparams > requires(optional) ( params ) specs requires(optional) { body }
//(4)  (since C++23)
[ captures ] < tparams > requires(optional) specs { body }
//其中 capture 是捕获列表,params 是参数表,
//optional是函数选项,ret 是返回值类型,body是函数体。
//specs  -  consists of specifiers, exception, attr and trailing-return-type in that order; each of these components is optional

c. 捕获方式 (Capture Modes)

在这里插入图片描述

捕获列表支持多种捕获模式,包括值捕获、引用捕获、隐式值捕获和隐式引用捕获。值捕获是以传值方式捕获变量,这意味着在Lambda表达式中使用的是变量的副本。引用捕获是以传引用方式捕获变量,这意味着在Lambda表达式中使用的是变量的引用。隐式值捕获和隐式引用捕获则可以一次性捕获所有变量,分别使用=, &表示。捕获列表还可以混合使用这些捕获模式,根据实际需要灵活选择。

零个或多个捕获的逗号分隔列表,可选择以捕获默认值开头。

lambda 表达式还可以通过捕获列表捕获一定范围内的变量:

  • [] 不捕获任何变量。
  • [&] 捕获外部作用域中所有变量,并作为引用在函数体中使用(按引用捕获)。
  • [=] 捕获外部作用域中所有变量,并作为副本在函数体中使用(按值捕获)。
  • [=,&foo] 按值捕获外部作用域中所有变量,并按引用捕获 foo 变量。
  • [a, &b] 以值的方式捕获a,引用的方式捕获b,也可以捕获多个。
  • [bar] 按值捕获 bar 变量,同时不捕获其他变量。
  • [this] 捕获当前类中的 this 指针,让 lambda 表达式拥有和当前类成员函数同样的访问权限。如果已经使用了 & 或者 =,就默认添加此选项。捕获 this 的目的是可以在 lamda 中使用当前类的成员函数和成员变量。

d.捕获示例

假设有一个书本信息的列表,定义如下。我们想要找出其中 title 包含某个关键字(target)的书本的数量,可以通过标准库中的 std::count_if + Lambda 表达式来实现。

struct Book {int id; std::string title;double price;};
std::vector<Book> books;std::string target = "C++";  // 找出其中 title 包含“C++”的书本的数量
  • 按值捕获(Capture by Value)

    auto cnt = std::count_if(books.begin(), books.end(), [target](const Book& book) {return book.title.find(target) != std::string::npos;}); 
    
  • 按引用捕获(Capture by Reference)

    auto cnt =std::count_if(books.begin(), books.end(), [&target](const Book& book) {return book.title.find(target) != std::string::npos;}); 
    
  • 捕获列表初始化(Capture Initializers)

    C++ 14** 支持 lambda capture initializers**​

    // 按值捕获 target,但是在 Lambda 内部的变量名叫做 v
    auto cnt =std::count_if(books.begin(), books.end(), [v = target](const Book& book) {return book.title.find(v) != std::string::npos;}); // 按引用捕获 target,但是在 Lambda 内部的名字叫做 r
    auto cnt =std::count_if(books.begin(), books.end(), [&r = target](const Book& book) {return book.title.find(r) != std::string::npos;}); 
    

e. 使用场景与优势 (Use Cases and Advantages)

Lambda表达式的使用场景包括但不限于:替换小型函数、简化STL算法和函数适配器、实现回调函数和事件处理、以及简化并行和异步编程。使用Lambda表达式的优势在于:

  • 简化语法,提高代码可读性和可维护性
    不需要额外再写一个函数或者函数对象,避免了代码膨胀和功能分散,让开发者更加集中精力在手边的问题,同时也获取了更高的生产率。

  • 更好的性能,编译器可以进行内联优化

  • 声明式编程风格:就地匿名定义目标函数或函数对象,不需要额外写一个命名函数或者函数对象.减少代码冗余。

  • 以更直接的方式去写程序,好的可读性和可维护性.更好地支持函数式编程范式,使代码更加通用和可复用

  • 在需要的时间和地点实现功能闭包,使程序更灵活。

实际应用案例 (Practical Examples)

a.使用Lambda表达式简化算法 (Simplifying Algorithms with Lambda Expressions)

C++标准库中包含许多算法,如sortfor_eachtransform等,使用Lambda表达式可以使这些算法更加简洁和灵活。例如,对一个整数向量进行排序,我们可以使用Lambda表达式自定义排序规则:

std::vector<int> numbers = {3, 1, 4, 1, 5, 9, 2, 6, 5};
std::sort(numbers.begin(), numbers.end(), [](int a, int b) { return a > b; });

b. 在容器操作中使用Lambda (Using Lambda in Container Operations)

Lambda表达式可以与C++标准库中的容器结合使用,实现更加简洁和高效的容器操作。例如,我们可以使用std::for_each遍历一个向量,并将其中的每个元素加倍:

std::vector<int> numbers = {1, 2, 3, 4, 5};
std::for_each(numbers.begin(), numbers.end(), [](int &n) { n *= 2; });

c. 异步编程与Lambda (Asynchronous Programming and Lambda)

在异步编程中,Lambda表达式可以作为回调函数或任务,简化异步任务的创建和调度。例如,我们可以使用std::async启动一个异步任务,计算斐波那契数列的第n项:

#include <future>auto fibonacci = [](int n) {int a = 0, b = 1;for (int i = 0; i < n; ++i) {int temp = a;a = b;b = temp + b;}return a;
};std::future<int> result = std::async(std::launch::async, fibonacci, 10);
int value = result.get(); // 获取异步任务的结果

通过这些实际应用案例,我们可以看到Lambda表达式在编程中的强大功能和优势。

Lambda表达式的高级用法 (Advanced Usage of Lambda Expressions)

a. Lambda表达式中的条件表达式 (Conditional Expressions in Lambda)

Lambda表达式可以使用条件表达式进行复杂的逻辑判断,例如实现多种排序规则:

auto custom_sort = [](bool ascending) {return [ascending](int a, int b) {return ascending ? a < b : a > b;};
};std::vector<int> numbers = {3, 1, 4, 1, 5, 9, 2, 6, 5};
std::sort(numbers.begin(), numbers.end(), custom_sort(true)); // 升序排序
std::sort(numbers.begin(), numbers.end(), custom_sort(false)); // 降序排序

b. 嵌套Lambda表达式 (Nested Lambda Expressions)

Lambda表达式可以嵌套在其他Lambda表达式中,实现高级功能。例如,下面的代码定义了一个高阶函数compose,用于组合两个函数:

auto compose = [](auto f1, auto f2) {return [f1, f2](auto x) { return f1(f2(x)); };
};auto square = [](int x) { return x * x; };
auto increment = [](int x) { return x + 1; };auto square_then_increment = compose(increment, square);
auto result = square_then_increment(3); // 结果为10 (3 * 3 + 1)

c. 使用Lambda表达式实现惰性求值 (Lazy Evaluation with Lambda Expressions)

Lambda表达式可以用于实现惰性求值,即在需要结果的时候才进行计算。这在处理大量数据或计算代价较高的场景中非常有用。例如,我们可以使用Lambda表达式实现一个惰性求和函数:

auto lazy_sum = [](auto container) {return [container]() {return std::accumulate(container.begin(), container.end(), 0);};
};std::vector<int> numbers = {1, 2, 3, 4, 5};
auto sum = lazy_sum(numbers);
// ... 其他操作
int result = sum(); // 在需要时进行求和计算

以上高级用法展示了Lambda表达式在实际编程中的强大潜力,它们可以帮助我们编写出更简洁、高效的代码。这些技巧只是Lambda表达式的冰山一角,掌握了这些高级用法,将有助于您更好地发挥Lambda表达式的威力。

Qt中的lambda运用

qt创建线程使用lambda表达式

Qt中创建线程可以使用QThread类。在某些情况下,我们可能需要使用lambda表达式与QThread结合。例如,当我们想要在子线程中运行简单的任务时,可以利用lambda表达式实现。以下是一个使用lambda表达式与QThread结合的例子:

#include <QCoreApplication>
#include <QThread>
#include <QDebug>int main(int argc, char *argv[])
{QCoreApplication app(argc, argv);QThread *thread = new QThread();QObject::connect(thread, &QThread::started, [&]() {qDebug() << "Thread started, running tasks...";// 在这里执行任务// ...qDebug() << "Tasks completed.";thread->quit(); // 结束线程});QObject::connect(thread, &QThread::finished, [&]() {qDebug() << "Thread finished.";thread->deleteLater(); // 删除线程对象app.quit(); // 退出应用});thread->start(); // 开始线程return app.exec();
}

在这个例子中,我们创建了一个QThread对象,并通过lambda表达式将其started信号与一个匿名函数连接,以便在子线程中执行任务。同样地,我们将finished信号与另一个lambda表达式连接,以便在子线程完成时执行相应的操作。

尽管如此,这种方法并不是Qt推荐的线程使用方式。在实际项目中,Qt建议使用QThread的子类化方法,并将耗时任务放在子类的run()方法中。不过,对于一些简单的场景,使用lambda表达式与QThread结合仍然是一个方便的选择。

connect使用lambda表达式

在Qt中,使用connect()函数连接信号和槽是非常常见的。通常情况下,我们需要定义槽函数并将其与信号关联。然而,在某些情况下,为了简化代码或者方便快速实现功能,我们可以使用lambda表达式作为槽函数。以下是关于在connect()中使用lambda表达式的一些详细内容:

  1. 基本用法: 使用lambda表达式作为槽函数时,我们不再需要在类中定义槽函数。而是可以直接在connect()中编写槽函数逻辑。基本语法如下:
    connect(sender, &SenderClass::signal, this, [this]() {// 槽函数逻辑
    });
    例如,我们可以在一个按钮的clicked信号中使用lambda表达式:
    QPushButton *button = new QPushButton("Click me");
    connect(button, &QPushButton::clicked, this, [this]() {qDebug() << "Button clicked!";
    });
  2. 捕获列表: 在lambda表达式中,我们可以使用捕获列表来捕获外部变量。捕获列表支持值捕获和引用捕获。[=]表示值捕获,[&]表示引用捕获。我们还可以选择捕获特定的变量,例如[this, &var1, var2]
    int value = 0;
    connect(button, &QPushButton::clicked, this, [this, value]() {qDebug() << "Button clicked with value:" << value;
    });
  3. 带参数的槽函数: lambda表达式可以接受参数,类似于普通函数。这使得我们可以在信号槽连接中捕获信号传递的参数。
    QSlider *slider = new QSlider(Qt::Horizontal);
    connect(slider, &QSlider::valueChanged, this, [this](int value) {qDebug() << "Slider value changed:" << value;
    });
  4. 使用mutable关键字: 在某些情况下,我们可能需要修改捕获的变量。默认情况下,lambda表达式不允许修改值捕获的变量。为了允许修改值捕获的变量,我们需要使用mutable关键字。
    int counter = 0;
    connect(button, &QPushButton::clicked, this, [=]() mutable {counter++;qDebug() << "Button clicked" << counter << "times";
    });

异步操作使用lambda表达式

在Qt中,某些类可以利用lambda表达式进行异步操作。这种方法可以让我们在不定义槽函数的情况下,更简洁地处理异步事件。以下是一些使用lambda表达式进行异步操作的例子:

  1. QTimer:

    QTimer提供了定时器功能,可以在给定的时间间隔后发出超时信号。通常我们会将QTimer的超时信号与槽函数连接。然而,使用lambda表达式,我们可以直接在连接时定义回调逻辑。

    例如,我们可以使用QTimer的singleShot()函数创建一个只触发一次的定时器:

    QTimer::singleShot(1000, [this]() {qDebug() << "One second has passed!";
    });

    在这个例子中,当定时器触发时,lambda表达式中的代码将被执行。

    如果我们需要一个重复触发的定时器,可以这样做:

    QTimer *timer = new QTimer(this);
    timer->setInterval(1000);connect(timer, &QTimer::timeout, this, [this]() {qDebug() << "One second has passed!";
    });timer->start();
  2. QFutureWatcher:

    QFutureWatcher可以用于监视异步计算任务。当任务完成时,QFutureWatcher会发出相应的信号。我们可以将这些信号与lambda表达式连接,以便在任务完成时执行回调逻辑。

    例如,我们可以在一个异步任务完成时,使用lambda表达式处理结果:

    QFutureWatcher<int> *watcher = new QFutureWatcher<int>(this);connect(watcher, &QFutureWatcher<int>::finished, this, [this, watcher]() {int result = watcher->result();qDebug() << "Task completed with result:" << result;
    });QFuture<int> future = QtConcurrent::run([]() {// 异步任务逻辑return 42;
    });watcher->setFuture(future);

    在这个例子中,我们使用QtConcurrent::run()启动了一个异步任务。当任务完成时,QFutureWatcher的finished信号将被触发。我们使用lambda表达式连接了这个信号,并在其中处理任务的结果。

    这些例子展示了如何在Qt中使用lambda表达式进行异步操作。利用lambda表达式可以让我们更简洁地处理异步事件,提高代码可读性。然而,同样需要注意在实际项目中不要过度使用lambda表达式,以免导致代码难以理解和维护。

Lambda表达式的性能与效率 (Performance and Efficiency of Lambda Expressions)

a. 编译器优化 (Compiler Optimizations)

Lambda表达式在运行时通常具有较高的性能,因为编译器可以对它们进行优化。例如,内联优化是一种常见的优化手段,可以减少函数调用的开销。通过将Lambda表达式内联展开,编译器可以有效地优化生成的代码,提高运行时性能。

b. 常见陷阱与注意事项 (Common Pitfalls and Precautions)

  • lambda函数是一个closure(闭包)类型的函数;
  • lambda函数在编译时进行转换
  • lambda捕获的值是否可以修改,需要确认是否有mutable修饰。
  • 默认引用捕获可能带来的悬挂引用问题
  • 引用捕获陷阱:引用捕获[&]别使用****局部变量
  • this陷阱:lambda里避免有全局变量或静态变量或者比当前类生命周期更长****的变量
  • 尽量避免使用****复杂的lambda

尽管Lambda表达式具有诸多优势,但在使用过程中仍需注意一些陷阱和注意事项:

  1. 捕获列表中的生命周期:当使用引用捕获变量时,务必确保捕获的变量在Lambda表达式执行期间仍然有效。否则,可能导致未定义行为。
  2. 闭包的复制与传递:Lambda表达式生成的闭包对象可以像普通对象一样进行复制和传递。然而,需要注意的是,当闭包对象捕获了大量数据时,复制和传递可能导致性能下降。
  3. Lambda表达式的递归调用:Lambda表达式本身无法直接进行递归调用,因为它没有名称。在需要递归调用时,可以使用std::function包装Lambda表达式,然后进行递归调用。

常见问题与答疑 (Frequently Asked Questions)

在使用Lambda表达式时,很多开发者可能会遇到一些问题。在本章节中,我们将回答一些与Lambda表达式相关的常见问题。

a. 如何将Lambda表达式作为函数参数传递? (How to pass a Lambda expression as a function parameter?)

传递Lambda表达式作为函数参数时,我们可以使用std::function和模板参数两种方式。std::function提供了一种通用的函数类型,可以容纳各种可调用对象,包括Lambda表达式。使用模板参数可以保留Lambda表达式的类型信息,避免额外的性能开销。

#include <functional>// 使用std::function作为参数
void execute_function(const std::function<int(int, int)> &func, int x, int y) {int result = func(x, y);// ... 其他操作
}// 使用模板参数作为参数
template <typename Callable>
void execute_template(const Callable &func, int x, int y) {int result = func(x, y);// ... 其他操作
}auto sum = [](int a, int b) { return a + b; };
execute_function(sum, 3, 5);
execute_template(sum, 3, 5);

b. Lambda表达式如何访问类成员变量? (How can a Lambda expression access class member variables?)

Lambda表达式可以通过捕获类的this指针来访问成员变量。以下代码展示了如何在Lambda表达式中访问类的成员变量:

class MyClass {
public:void modify_member() {auto modify_lambda = [this] { member_ += 1; };modify_lambda();}private:int member_ = 0;
};

c. 如何在Lambda表达式中使用nothrow? (How to use nothrow in Lambda expressions?)

nothrow是C++中的一种异常规范,它表示函数不会抛出任何异常。在Lambda表达式中,可以通过在函数体之前添加nothrow关键字来指定异常规范:

auto nothrow_lambda = [](int x, int y) noexcept {return x + y;
};

请注意,nothrow仅用于指定函数的异常规范,并不意味着函数一定不会抛出异常。在实际编程中,我们需要确保Lambda表达式的实现与声明的异常规范一致。

d. 如何创建可变Lambda表达式? (How to create a mutable Lambda expression?)

默认情况下,Lambda表达式中的所有变量都是const,因此无法在Lambda表达式中修改捕获的变量。然而,我们可以使用mutable关键字来创建可变Lambda表达式,这样可以修改捕获的值变量:

int counter = 0;
auto mutable_lambda = [counter]() mutable {counter++;std::cout << "Counter: " << counter << std::endl;
};mutable_lambda(); // 输出:Counter: 1
mutable_lambda(); // 输出:Counter: 2

请注意,mutable关键字仅适用于值捕获的变量。对于引用捕获的变量,不需要使用mutable关键字,因为它们本身就可以被修改。

e. 如何将Lambda表达式转换为函数指针? (How to convert a Lambda expression to a function pointer?)

仅在Lambda表达式不捕获任何外部变量时,它才可以被转换为函数指针。以下代码展示了如何将Lambda表达式转换为函数指针:

auto lambda = [](int x, int y) { return x + y; };
using FunctionPtrType = int (*)(int, int);
FunctionPtrType func_ptr = lambda;int result = func_ptr(3, 5); // 结果为8

需要注意的是,如果Lambda表达式捕获了外部变量,将无法转换为函数指针。

f. Lambda表达式能否捕获全局变量? (Can a Lambda expression capture global variables?)

Lambda表达式不能直接捕获全局变量,因为全局变量在整个程序的生命周期内都是可访问的。然而,您可以在Lambda表达式中直接使用全局变量。以下代码展示了如何在Lambda表达式中访问全局变量:

#include <iostream>int global_variable = 0;int main() {auto lambda = [] {global_variable += 1;std::cout << "Global variable: " << global_variable << std::endl;};lambda(); // 输出:Global variable: 1lambda(); // 输出:Global variable: 2return 0;
}

通过这些额外的问题答疑,我们希望能够帮助您在使用Lambda表达式时遇到的其他问题,使您更加熟练地运用Lambda表达式解决实际编程问题。

C++标准的发展与Lambda表达式 (Evolution of C++ Standards and Lambda Expressions)

a.C++11中的Lambda (Lambda in C++11)

C++11标准首次引入了Lambda表达式,为C++程序员提供了一种简洁、强大的编程工具。C++11中的Lambda表达式支持基本的值捕获、引用捕获和自动类型推导等功能。

b. C++14中的Lambda扩展 (Lambda Extensions in C++14)

C++14对Lambda表达式进行了扩展,引入了泛型Lambda和自动返回类型推导等新特性。泛型Lambda允许使用auto作为参数类型,使得Lambda表达式具有更好的通用性和可复用性。

c. C++17与C++20中的新增功能 (New Features in C++17 and C++20)

C++17和C++20标准中进一步完善了Lambda表达式,包括引入constexpr Lambda和模板参数捕获等功能。这些改进使得Lambda表达式更加强大和灵活,可以应对更多的编程场景。

从编译器底层和内存角度看Lambda表达式及其与其他函数的区别(Lambda expressions and their differences from other functions from the Perspective of Compiler bottom layer and memory)

在这一章节中,我们将深入探讨Lambda表达式在编译器底层和内存方面的工作原理,以及它与其他函数之间的差异。

// Lambda表达式
auto lambda = [x](int y) { return x + y; };// 底层闭包类型实现
class Closure {
public:explicit Closure(int x) : x_(x) {}int operator()(int y) const { return x_ + y; }private:int x_;
};

a. Lambda表达式的底层实现 (Underlying Implementation of Lambda Expressions)

Lambda表达式在底层被编译器转换为一个匿名类(称为闭包类型),该类具有一个重载的调用运算符。Lambda表达式捕获的变量会被嵌入到闭包类型的数据成员中。以下是一个简化的Lambda表达式的底层实现示例:

// Lambda表达式
auto lambda = [x](int y) { return x + y; };// 底层闭包类型实现
class Closure {
public:explicit Closure(int x) : x_(x) {}int operator()(int y) const { return x_ + y; }private:int x_;
};

b. 内存使用 (Memory Usage)

Lambda表达式的内存使用取决于它捕获的变量以及是否使用引用捕获。值捕获的变量会被复制到闭包类型的数据成员中,而引用捕获的变量则以引用形式存储。使用引用捕获通常可以减少内存使用,但需要注意引用的生命周期。

此外,如果Lambda表达式没有捕获任何外部变量,则其内存使用与普通函数相似。在这种情况下,编译器可能会为其生成与普通函数相同的代码。

c. 与其他函数的区别 (Difference from Other Functions)

Lambda表达式与普通函数和函数对象(functors)在底层实现和内存使用上有一定的差异:

  • 普通函数:普通函数不包含任何状态信息,且在底层实现上更简单。然而,普通函数不能直接捕获外部变量,通常需要使用函数参数来传递所需的信息。
  • 函数对象(functors):函数对象类似于Lambda表达式,它们都是通过类实现的,具有重载的调用运算符。不过,与Lambda表达式相比,手动实现函数对象通常更加繁琐,而Lambda表达式的语法更为简洁。

总之,Lambda表达式作为一种强大的编程工具,其在编译器底层和内存使用上表现出一定的灵活性和优势。相较于普通函数和函数对象,Lambda表达式提供了更简洁的语法和更方便的捕获机制,使得程序员能够更高效地编写代码。

结语

从心理学的角度来看,使用Lambda表达式具有一系列优势,使得程序员在编程过程中能够更加高效、聚焦,并降低心理负担。

  1. 降低心理负担:Lambda表达式的简洁性使得代码更加易读、易懂。编写简洁的代码有助于降低程序员在阅读、理解和维护代码时的心理负担。同时,它还有助于减少错误和潜在的bug。
  2. 提高认知效率:Lambda表达式通过将相关的代码逻辑组织在一起,能够提高程序员在处理复杂问题时的认知效率。使用Lambda表达式可以帮助程序员将注意力集中在重要的业务逻辑上,而无需过多关注实现细节。
  3. 创造力的激发:Lambda表达式的灵活性和表达能力使得程序员可以更加自由地发挥创意,设计出富有创造力的解决方案。这有助于激发程序员在编程过程中的创造力和探索精神。
  4. 高度的自主性与满足感:Lambda表达式提供了一种高度自主的编程方法,使得程序员能够根据实际需求和个人风格来灵活选择使用Lambda表达式。当程序员成功运用Lambda表达式解决问题时,他们将获得更高的成就感和满足感。
  5. 提升编程效率:Lambda表达式可以简化许多编程任务,使得程序员能够更快地完成项目。这有助于提高整体的编程效率,使得团队在开发过程中能够更好地协同合作,实现更高的生产力。

综上所述,Lambda表达式作为一种强大的编程工具,从心理学的角度来看,它有助于提高程序员的认知效率、激发创造力、降低心理负担、提升自主性和满足感,以及提高编程效率。因此,学习并掌握Lambda表达式对于程序员而言具有重要的价值。

相关文章:

C ++匿名函数:揭开C++ Lambda表达式的神秘面纱

潜意识编程&#xff1a;揭秘C Lambda表达式的神秘面纱 Subconscious Programming: Unveiling the Mystery of C Lambda Expressions 引言&#xff1a;Lambda表达式的魅力 (The Charm of C Lambda Expressions)Lambda表达式简介与基本概念 (Introduction and Basic Concepts of …...

AOP使用场景记录总结(缓慢补充更新中)

测试项目结构: 目前是测试两个日志记录和 代码的性能测试 后面如果有其他的应用场景了在添加.其实一中就包括了二,但是没事,多练一遍 1. 日志记录 比如说对service层中的所有增加,删除,修改方法添加日志, 记录内容包括操作的时间 操作的方法, 方法的参数, 方法所在的类, 方法…...

FPGA基于XDMA实现PCIE X4的HDMI视频采集 提供工程源码和QT上位机程序和技术支持

目录1、前言2、我已有的PCIE方案3、PCIE理论4、总体设计思路和方案5、vivado工程详解6、驱动安装7、QT上位机软件8、上板调试验证9、福利&#xff1a;工程代码的获取1、前言 PCIE&#xff08;PCI Express&#xff09;采用了目前业内流行的点对点串行连接&#xff0c;比起 PCI …...

ArcGIS、ENVI、InVEST、FRAGSTATS等多技术融合提升环境、生态、水文、土地、土壤、农业、大气等领域的数据分析

查看原文>>>ArcGIS、ENVI、InVEST、FRAGSTATS等多技术融合提升环境、生态、水文、土地、土壤、农业、大气等领域的数据分析 目录 专题一、空间数据获取与制图 专题二、ArcGIS专题地图制作 专题三、空间数据采集与处理 专题四、遥感数据处理与应用 专题五、DEM数据…...

怎么找回回收站里已经删除的文件

作为忙忙碌碌的打工人&#xff0c;电脑办公是在所难免的&#xff0c;而将使电脑存储着大量的数据文件&#xff0c;不少小伙伴都养成了定期清理电脑的习惯。而清理简单快捷的方法&#xff0c;无疑是直接把文件拖进回收站里。再一键清空&#xff0c;清理工作就完成了。但如果发现…...

Spring Boot、Cloud、Alibaba 版本说明

Spring Boot、Cloud、Alibaba 版本说明 一、毕业版本依赖关系(推荐使用) 由于 Spring Boot 3.0&#xff0c;Spring Boot 2.7~2.4 和 2.4 以下版本之间变化较大&#xff0c;目前企业级客户老项目相关 Spring Boot 版本仍停留在 Spring Boot 2.4 以下&#xff0c;为了同时满足存…...

软件测试入门第一步:编写测试报告

什么是测试报告&#xff1f; 1、说明&#xff1a;是指把测试的过程和结果写成文档&#xff0c;对发现的问题和缺陷进行分析&#xff0c;为纠正软件的存在的质量问题提供依据&#xff0c;同时为软件验收和交付打下基础。 ps. 【测试过程和测试结果的分析报告&#xff0c;以及上线…...

【Vue】vue中的路由导航守卫(路由的生命周期)

文章目录全局前置守卫可选的第三个参数 next全局解析守卫router.beforeResolve全局后置钩子路由独享的守卫组件内的守卫可用的配置 API使用组合 API完整的导航解析流程正如其名&#xff0c;vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。这里有很多方式植入…...

NumPy 基础知识 :6~10

原文&#xff1a;Numpy Essentials 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 六、NumPy 中的傅立叶分析 除其他事项外&#xff0c;傅立叶分析通常用于数字信号处理。 这要归功于它在将输入信号&#xff08;时域&#xff09;分离为以离散频率&#xff08;频域&am…...

实现vue的条件渲染

我的需求是根据设备不同的状态 渲染不同的标签。设备状态用device_State表示。 在线上面是一个vue的标签&#xff0c;我有一个数据state &#xff0c;如何让这个标签根据数据的取值 &#xff0c;修改内容&#xff0c;如state1时&#xff0c;标签修改为离线 要根据数据的取值动态…...

第四章 word2vec 的高速化

目录4.1 word2vec 的改进①4.1.1 Embedding 层4.1.2 Embedding 层的实现4.2 word2vec 的改进②4.2.1 中间层之后的计算问题4.2.2 从多分类到二分类4.2.3 sigmoid 函数和交叉熵误差4.2.4 多分类到二分类的实现4.2.5 负采样4.2.6 负采样的采样方法4.2.7 负采样的实现4.3 改进版 w…...

【四】3D Object Model之创建Creation——clear_object_model_3d()/copy_object_model_3d()算子

&#x1f60a;&#x1f60a;&#x1f60a;欢迎来到本博客&#x1f60a;&#x1f60a;&#x1f60a; &#x1f31f;&#x1f31f;&#x1f31f; Halcon算子太多&#xff0c;学习查找都没有系统的学习查找路径&#xff0c;本专栏主要分享Halcon各类算子含义及用法&#xff0c;有…...

第三十一章 配置镜像 - 删除镜像成员时删除镜像数据库属性

文章目录第三十一章 配置镜像 - 删除镜像成员时删除镜像数据库属性删除镜像成员时删除镜像数据库属性编辑或删除异步成员第三十一章 配置镜像 - 删除镜像成员时删除镜像数据库属性 删除镜像成员时删除镜像数据库属性 当从镜像中删除成员时&#xff0c;始终可以选择从属于该镜…...

自动写作ai-自动写作神器

自动生成文章 自动生成文章是指使用自然语言处理和人工智能技术&#xff0c;通过算法来自动生成文章的过程。一些自动生成文章的工具可以使用大量数据&#xff0c;学习数据背后的语言规范和知识结构&#xff0c;从而生成高质量和有用的文章。这种技术能够减少写作时间和人力成…...

P1368 【模板】最小表示法(SAM 求最小循环移位)

【模板】最小表示法 题目描述 小敏和小燕是一对好朋友。 他们正在玩一种神奇的游戏&#xff0c;叫 Minecraft。 他们现在要做一个由方块构成的长条工艺品。但是方块现在是乱的&#xff0c;而且由于机器的要求&#xff0c;他们只能做到把这个工艺品最左边的方块放到最右边。…...

投票感知器参数学习算法

投票感知器参数学习算法 以下为投票感知器参数学习算法的伪代码&#xff1a; 输入&#xff1a;训练集 (x1,y1),(x2,y2),...,(xn,yn)(x_1, y_1), (x_2, y_2), ..., (x_n, y_n)(x1​,y1​),(x2​,y2​),...,(xn​,yn​)&#xff0c;学习率 η\etaη&#xff0c;最大迭代次数 TTT…...

Hyper-v下安装CentOS-Stream-9

1、我不想要动态扩展的硬盘&#xff0c;固定大小硬盘性能更高&#xff0c;所以这里我先创建一个固定硬盘&#xff08;如果你想用动态扩展的硬盘&#xff0c;那么可以省略前面几步&#xff0c;直接从第7步开始&#xff0c;并在第12步选择创建可动态扩展的虚拟硬盘&#xff09;&a…...

数据结构之顺序表,实现顺序表的增删改查

目录 一、顺序表的概念 二、顺序表的分类 1.静态顺序表 2.动态顺序表 3.顺序表的增删改查 总结 一、顺序表的概念 顺序表是一段物理地址连续的村塾单元依次存储数据元素的线性结构&#xff0c;一般情况下使用数组存储&#xff0c;在数组上完成数据的增删改查。 二、顺…...

HTB-Jeeves

HTB-Jeeves信息收集80端口50000端口![在这里插入图片描述](https://img-blog.csdnimg.cn/5824bf345bc040ee9e449bebeade9495.png)开机kohsuke -> Administrator信息收集 80端口 ask jeeves是一款以回答用户问题提问的自然语言引擎&#xff0c;面对问题首先查看数据库里是否…...

大力出奇迹——GPT系列论文学习(GPT,GPT2,GPT3,InstructGPT)

目录说在前面1.GPT1.1 引言1.2 训练范式1.2.1 无监督预训练1.2.2 有监督微调1.3 实验2. GPT22.1 引言2.2 模型结构2.3 训练范式2.4 实验3.GPT33.1引言3.2 模型结构3.3 训练范式3.4 实验3.4.1数据集3.5 局限性4. InstructGPT4.1 引言4.2 方法4.2.1 数据收集4.2.2 各部分模型4.3 …...

Linux ubuntu更新meson版本

问题描述 在对项目源码用meson进行编译时&#xff0c;可能出现以下错误 meson.build:1:0: ERROR: Meson version is 0.45.1 but project requires > 0.58.0. 或者 meson_options.txt:1:0: ERROR: Unknown type feature. 等等&#xff0c;原因是meson版本跟设置的不适配。 …...

匹配yyyy-MM-dd日期格式的正则表达式

^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$ 解释&#xff1a; ^&#xff1a;匹配行的开头 \d{4}&#xff1a;匹配四个数字&#xff0c;表示年份 -&#xff1a;匹配一个横杠 (0[1-9]|1[0-2])&#xff1a;匹配01到12的月份&#xff0c;0开头的要匹配两位数字&#xff0c;1开…...

【失业预告】生成式人工智能 (GAI)AIGC

文章目录AIGCGAIAGI应用1. 计算机领域2. 金融领域3. 电商领域4. C端娱乐5. 游戏领域6. 教育领域7. 工业领域8. 医疗领域9. 法律领域10. 农业/食品领域11. 艺术/设计领域来源AIGC AIGC&#xff0c;全称为Artificial Intelligence Generated Content&#xff0c;是一种新型的人工…...

TensorFlow 2.0 的新增功能:第一、二部分

原文&#xff1a;What’s New in TensorFlow 2.0 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【ApacheCN 深度学习 译文集】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 不要担心自己的形象&#xff0c;只关心如何实现目…...

Spring Boot配置文件详解

前言 Spring Boot 官方提供了两种常用的配置文件格式&#xff0c;分别是properties、YML格式。相比于properties来说&#xff0c;YML更加年轻&#xff0c;层级也是更加分明。 1. properties格式简介 常见的一种配置文件格式&#xff0c;Spring中也是用这种格式&#xff0c;语…...

实习面试题整理1

1、进行一下自我介绍 2、介绍一下你简历里的两个项目 3、说说vue的生命周期&#xff08;具体作用&#xff09; 4、说说你对vue单页面和多页面应用的理解 5、说说vue里自带的数组方法&#xff08;七种&#xff0c;往响应式数据上靠&#xff09; 6、说说vue双向数据绑定&…...

最新阿里、腾讯、华为、字节等大厂的薪资和职级对比,看看你差了多少...

互联网大厂新入职员工各职级薪资对应表(技术线)~ 最新阿里、腾讯、华为、字节跳动等大厂的薪资和职级对比 上面的表格不排除有很极端的收入情况&#xff0c;但至少能囊括一部分同职级的收入。这个表是“技术线”新入职员工的职级和薪资情况&#xff0c;非技术线(如产品、运营、…...

OpenCV——常用函数

cv::circle(overlay, pt, 2, cv::Scalar(0,green,red),-1); 使用OpenCV库中的circle()函数在图像上绘制圆形的代码。 具体来说&#xff0c;它的参数如下&#xff1a; - overlay&#xff1a;图像&#xff0c;在该图像上绘制圆形&#xff1b; - pt&#xff1a;圆心位置的cv:…...

超详细从入门到精通,pytest自动化测试框架实战-fixture多样玩法(九)

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 在编写测试用例&…...

OJ练习第70题——困于环中的机器人

困于环中的机器人 力扣链接&#xff1a;1041. 困于环中的机器人 题目描述 在无限的平面上&#xff0c;机器人最初位于 (0, 0) 处&#xff0c;面朝北方。注意: 北方向 是y轴的正方向。 南方向 是y轴的负方向。 东方向 是x轴的正方向。 西方向 是x轴的负方向。 机器人可以接受…...