草庐IT

释放 C++ 中 Lambda 表达式的强大威力

Linux迷 2023-03-28 原文
​Lambda 是 C++ 中的匿名函数(即没有函数名的函数),可以在定义和使用时内联。它们提供了一种方便的方式,将小的代码块作为参数传递给其他函数,或者在不需要创建命名函数的情况下定义一个函数。

​Lambda 在 C++ 中提供了一种灵活而简洁的方式来编写类似函数的对象,并在现代 C++ 编程中被广泛使用。

Lambda 的定义使用以下语法:

[ capture list ] ( argument list ) -> return type { function body }
捕获列表(capture list)用于指定 lambda 中可访问的来自外部作用域的变量。变量可以通过值捕获、引用捕获或使用 this 捕获。

  • 参数列表(argument list)指定将递给 lambda 的参数。
  • 返回类型(return type)指定 lambda 将返回的值的类型。如果未指定,则编译器将尝试推断其类型。
  • 函数体(function body)指定 lambda 被调用时将执行的代码。
以下是在C++中使用 lambda 的几种不同方式。

1、函数回调

函数回调是将一个函数作为参数传递给另一个函数,并在接收函数稍后的时间调用该函数。您可以将 lambda 作为函数参数传递,其中它将在发生某个事件时执行。

示例:

#include <iostream>
#include <algorithm>
#include <vector>

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};

// Lambda 表达式,用于查找两个数字的和
auto sum = [](int a, int b) { return a + b; };

int result = std::accumulate(numbers.begin(), numbers.end(), 0, sum);
std::cout << "Sum of elements in the vector: " << result << std::endl;

return 0;
}
在这个例子中,sum 变量是一个Lambda表达式,它接受两个参数 a 和 b,并返回它们的总和。std::accumulate 函数接受数字向量、结果的初始值和 sum 函数(Lambda 表达式)。该函数计算向量中所有元素的总和并返回结果,该结果被打印在屏幕上。

另一个例子:

#include <iostream>
#include <algorithm>
#include <vector>

int main()
{
std::vector<int> vec = { 1, 2, 3, 4, 5 };
int sum = 0;
std::for_each(vec.begin(), vec.end(), [&sum](int x) { sum += x; });
std::cout << "The sum is: " << sum << std::endl;
return 0;
}
在这种情况下,lambda 表达式 [&sum](int x) { sum += x; } 被传递作为要应用于每个元素的函数。lambda 通过引用 & 捕获变量 sum,以便可以在 lambda 主体中进行修改。

两个例子都达到了相同的结果,但第二个例子使用了 std::for_each 算法和 lambda 表达式,这是在 C++ 中更现代、更简洁的技术。

2、默认捕获

当一个 lambda 表达式被声明时没有任何显式的捕获,其默认行为是通过引用捕获周围作用域中的变量。这被称为默认捕获。

例子:

#include <iostream>

int main() {
int x = 42;
auto f = [ ]() { std::cout << x << std::endl; };
f();
return 0;
}
#include <iostream>

int main()
{
auto square = [](int x) { return x * x; };
std::cout << "The square of 5 is: " << square(5) << std::endl;
return 0;
}
在第二个例子中,定义了一个 lambda 表达式并将其存储在名为 square 的变量中。这个 lambda 表达式接受一个 int 类型的参数 x,并返回 x * x 的值,即参数的平方。

在主函数中,这个 lambda 表达式被用作函数对象。通过传递一个值为 5 的参数来调用它,并使用 cout 流显示结果。

3、按值捕获

这是 lambda 表达式的最简单形式,其中你通过值传递变量给函数。当一个变量被按值捕获时,它的当前值被存储在闭包中,而当周围作用域中的变量发生改变时,它的值不会被更新。这可以通过将变量包含在方括号 [ ] 中来实现。

例子:

#include <iostream>

int main() {
int x = 42;
auto f = [x]() { std::cout << x << std::endl; };
f();
return 0;
}
#include <iostream>

int main() {
int x = 42;
auto f = [x](int y) { std::cout << x+y << std::endl;};
f(1);
return 0;
}

4、按引用捕获

你可以通过使用 & 符号将变量传递给 lambda 表达式来按引用捕获变量。当一个变量被按引用捕获时,它的当前值被存储在闭包中,并且在周围作用域中变量发生变化时被更新。这是通过在方括号[ ]中在变量前加上取地址运算符 & 来实现的。

例子:

#include <iostream>

int main() {
int x = 42;
auto f = [&x]() { std::cout << x << std::endl; };
f();
return 0;
}
#include <iostream>

int main() {
int x = 10;

auto add_one = [&x]() { ++x; };
add_one();
std::cout << x << "\n";

return 0;
}
#include <iostream>

int main() {
int x = 42;
auto f = [&x]() { std::cout << x << std::endl; };
f();
return 0;
}

5、可变Lambda表达式

默认情况下,由 lambda 表达式捕获的变量是常量,不能在 lambda 表达式体内修改。如果你想要在 lambda 表达式中修改捕获的变量,你可以将 lambda 表达式设为可变。可变lambda允许捕获的变量被修改。这是通过在方括号 [ ] 中包含可变关键字来实现的。

例子:

#include <iostream>

int main() {
int x = 42;
auto f = [x]() mutable { std::cout << ++x << std::endl; };
f();
return 0;
}
lambda 表达式类似于普通函数,但它们有一些关键的区别。例如,lambda 表达式的类型没有被显式指定,但可以由编译器推断出来。此外,lambda 表达式可以从周围的作用域中捕获变量,这使得它们非常适用于创建闭包和在 C++ 中使用函数式编程概念。

与传统函数相比,lambda 表达式具有一些性能优势:

  • 内联函数:编译器会自动将 lambda 表达式内联,这意味着它们的代码直接插入到调用函数中。这可以减少函数调用的开销并提高性能。
  • 避免命名函数的开销:lambda 表达式没有名称,因此它们不必被声明和存储在符号表中,这可以减少开销并提高性能。
  • 改善高速缓存局部性:lambda 表达式可以在同一个函数中定义和使用,这意味着lambda使用的代码和数据将存储在与调用代码相同的高速缓存行中。这可以改善高速缓存局部性并降低高速缓存失效的成本。
  • 减小代码大小:lambda 表达式通常比命名函数小,并且它们不需要外部函数调用,这可以减小编译代码的大小并提高性能。
  • 增加灵活性:lambda 表达式可以用来将函数作为参数传递给其他函数,这提供了更大的灵活性,可以改善性能,减少重复代码的需求。
  • 提高可读性:lambda 表达式可以通过以紧凑而自包含的方式封装复杂的逻辑来使代码更易于阅读。这可以通过使代码更易于理解和维护来提高性能。
总之,与传统函数相比,lambdas 可以通过减少开销、提高缓存位置、减少代码大小、提高灵活性和提高可读性来提高性能。

有关释放 C++ 中 Lambda 表达式的强大威力的更多相关文章

  1. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  2. ruby - 正则表达式将非英文字母匹配为非单词字符 - 2

    @raw_array[i]=~/[\W]/非常简单的正则表达式。当我用一些非拉丁字母(具体来说是俄语)尝试时,条件是错误的。我能用它做什么? 最佳答案 @raw_array[i]=~/[\p{L}]/使用西里尔字符进行测试。引用:http://www.regular-expressions.info/unicode.html#prop 关于ruby-正则表达式将非英文字母匹配为非单词字符,我们在StackOverflow上找到一个类似的问题: https://

  3. ruby - 正则表达式在哪个位置失败? - 2

    我需要一个非常简单的字符串验证器来显示第一个符号与所需格式不对应的位置。我想使用正则表达式,但在这种情况下,我必须找到与表达式相对应的字符串停止的位置,但我找不到可以做到这一点的方法。(这一定是一种相当简单的方法……也许没有?)例如,如果我有正则表达式:/^Q+E+R+$/带字符串:"QQQQEEE2ER"期望的结果应该是7 最佳答案 一个想法:你可以做的是标记你的模式并用可选的嵌套捕获组编写它:^(Q+(E+(R+($)?)?)?)?然后你只需要计算你获得的捕获组的数量就可以知道正则表达式引擎在模式中停止的位置,你可以确定匹配结束

  4. ruby - 有没有办法从 ruby​​ case 语句中访问表达式? - 2

    我想从then子句中访问c​​ase语句表达式,即food="cheese"casefoodwhen"dip"then"carrotsticks"when"cheese"then"#{expr}crackers"else"mayo"end在这种情况下,expr是食物的当前值(value)。在这种情况下,我知道,我可以简单地访问变量food,但是在某些情况下,该值可能无法再访问(array.shift等)。除了将expr移出到局部变量然后访问它之外,是否有直接访问caseexpr值的方法?罗亚附注我知道这个具体示例很简单,只是一个示例场景。 最佳答案

  5. ruby - 正则表达式 - 排除一个字符 - 2

    这是一个例子:s="abcd+subtext@example.com"s.match(/+[^@]*/)Result=>"+subtext"问题是,我不想在其中包含“+”。我希望结果是“潜台词”,没有+ 最佳答案 您可以在正则表达式中使用括号来创建匹配组:s="abcd+subtext@example.com"s=~/\+([^@]*)/&&$1=>"subtext" 关于ruby-正则表达式-排除一个字符,我们在StackOverflow上找到一个类似的问题:

  6. ruby - 如何遍历 Ruby 中所有正则表达式匹配的字符串? - 2

    我们有一个字符串:“”这个正则表达式://i如何从当前字符串中获取所有匹配项? 最佳答案 "".scan(//)参见scan在ruby​​-docs上 关于ruby-如何遍历Ruby中所有正则表达式匹配的字符串?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/6857852/

  7. Ruby 正则表达式匹配逗号,但忽略括号中的逗号 - 2

    我正在尝试通过正则表达式拆分参数列表。这是一个带有我的参数列表的字符串:"a=b,c=3,d=[1,3,5,7],e,f=g"我想要的是:["a=b","c=3","d=[1,3,5,7]","e","f=g"]我试过先行,但Ruby不允许使用动态范围后行,所以这行不通:/(?如何让正则表达式忽略方括号中的所有内容? 最佳答案 也许这样的东西对你有用:str.scan(/(?:\[.*?\]|[^,])+/)编辑再三考虑。简单的非贪婪匹配器在某些嵌套括号的情况下会失败。 关于Ruby正则

  8. ruby - 查找重叠的正则表达式匹配项 - 2

    我想找到给定字符串中的所有匹配项,包括重叠匹配项。我怎样才能实现它?#Example"a-b-c-d".???(/\w-\w/)#=>["a-b","b-c","c-d"]expected#Solutionwithoutoverlappedresults"a-b-c-d".scan(/\w-\w/)#=>["a-b","c-d"],but"b-c"ismissing 最佳答案 在积极的前瞻中使用捕获:"a-b-c-d".scan(/(?=(\w-\w))/).flatten#=>["a-b","b-c","c-d"]参见Rubyde

  9. ruby - 了解在 Ruby 中与 lambda 一起使用的 inject 行为 - 2

    我经常将预配置的lambda插入可枚举的方法中,例如“map”、“select”等。但是“注入(inject)”的行为似乎有所不同。例如与mult4=lambda{|item|item*4}然后(5..10).map&mult4给我[20,24,28,32,36,40]但是,如果我制作一个2参数lambda用于像这样的注入(inject),multL=lambda{|product,n|product*n}我想说(5..10).inject(2)&multL因为“inject”有一个可选的单个初始值参数,但这给了我......irb(main):027:0>(5..10).inject

  10. ruby-on-rails - rails 中的正则表达式匹配 [\w] 和 "-"但不匹配数字 - 2

    我想为名字验证编写一个正则表达式。正则表达式应包括所有字母(拉丁/法语/德语字符等)。但是我想从中排除数字并允许-。所以基本上它是\w(减)数(加)-。请帮忙。 最佳答案 ^[\p{L}-]+$\p{L}匹配anykindofletterfromanylanguage. 关于ruby-on-rails-rails中的正则表达式匹配[\w]和"-"但不匹配数字,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.c

随机推荐