[转]每个开发人员都应该知道的一些很棒的现代C ++功能
每个开发人员都应该知道的一些很棒的现代C ++功能(原文作者:Mottakin)
作为一种语言,C ++已经发展了很多。
当然,这并非一overnight而就。曾经有一段时间,C ++缺乏活力。很难喜欢这种语言。
但是,当C ++标准委员会决定加快发展时,事情发生了变化。
自2011年以来,C ++已经成为许多人一直期望的动态且不断发展的语言。
不要误以为语言变得更容易了。它仍然是使用最广泛的最困难的编程语言之一,即使不是最困难的一种。但是C ++也比以前的版本更加易于使用。
在我的上一篇文章中,我谈到 了在过去几年中得到了丰富的 C ++算法库 (https://medium.freecodecamp.org/how-i-discovered-the-c-algorithm-library-and-learned-not-to-reinvent-the-wheel-2398a34e23e3)。
今天,我们将研究每个开发人员都想知道的一些新功能(从C ++ 11开始,已经有8年的历史了)。
还要注意,我在本文中略过了一些高级功能,但是我愿意在将来写它们。🤞️
去!
自动关键字
当C ++ 11首次引入时 ,生活变得更加轻松。 auto
的想法 是让C ++编译器在编译时推断出数据的类型—而不是让您在 每次中断时都声明该类型 。 当您拥有 😛 这样的数据类型时,这非常方便 auto
map<string,vector<pair<int,int>>>
查看行号5。没有,您不能声明某些内容 。这实际上是有道理的。第5行没有让编译器知道数据类型可以是什么。 initializer
最初,它 是有一定局限性的。然后,在该语言的更高版本中,添加了更多功能! auto
在第7和8行中,我使用了括号括起来的初始化。这也是C ++ 11中添加的新功能。
请记住,在使用的情况下, 编译器必须采用某种方式来推断出您的类型。 auto
现在是一个很好的问题, 如果我们写东西会 怎样?那是编译错误吗?那是向量吗? auto a = {1, 2, 3}
实际上,C ++ 11引入了 。如果声明,大括号初始化列表将被视为此轻量级容器 。 std::initializer_list<type>
auto
最后,正如我之前提到的,当您具有复杂的数据结构时,由编译器进行类型推导可能非常有用:
不要忘记签出第25行!该表达式 实际上是C ++ 17中的一个新功能。这称为 结构化绑定 。在该语言的早期版本中,您必须分别提取每个变量。但是结构化绑定使其更加方便。 auto [v1,v2] = itr.second
此外,如果您想使用引用获取数据,只需添加一个符号-即可 。 auto &[v1,v2] = itr.second
整洁的。
Lambda表达式
C ++ 11引入了lambda表达式,类似于JavaScript中的匿名函数。它们是函数对象,没有任何名称,并且它们 基于一些简洁的语法在各种 范围内 捕获变量 。它们也可以分配给变量。
如果您需要在代码中完成一些小的快速操作,但是您不愿意为此编写一个完整的单独函数,则Lambda非常有用。另一个很常见的用途是将它们用作比较功能。
上面的例子有很多话要说。
首先,请注意花括号初始化如何为您增加负担。然后是通用的 ,这也是C ++ 11中的新增功能。然后,lambda函数用作数据的比较器。声明了lambda函数的参数,该参数 是在C ++ 14中添加的。在此之前,我们不能使用 函数参数。 begin(), end()
auto
auto
注意我们如何用方括号开始lambda表达式 。它们定义了lambda的范围-它对局部变量和对象有多少权限。 []
正如 现代C ++上这个 出色的存储库中 (https://github.com/AnthonyCalandra/modern-cpp-features#lambda-expressions)所定义的 :
[]
-什么也没捕获。因此,您不能在lambda表达式内使用外部作用域的任何局部变量。您只能使用参数。[=]
—按值捕获作用域中的局部对象(局部变量,参数)。您可以使用它们,但不能修改它们。[&]
—通过引用捕获作用域中的局部对象(局部变量,参数)。您可以修改它们。像下面的例子。[this]
—this
按值捕获 指针。[a, &b]
— 通过引用a
,b
按值 捕获对象 。
因此,如果您想在lambda函数中将数据转换为其他格式,则可以利用作用域的优势来使用lambda。例如:
在上面的示例中,如果您 在lambda表达式中通过值()捕获了局部变量,则 无法 在第5行中进行更改 。因为简单地说,您无权执行此操作。不要滥用您的权利!😛 [factor]
factor
最后,请注意我们 作为参考。这样可以确保lambda函数内部的任何更改实际上都会更改 。 val
vector
if和switch内部的init语句
我一开始就很喜欢C ++ 17的这个功能。
因此,显然,您现在可以在 块内同时进行变量的初始化和检查条件 。这对于使代码简洁明了非常有帮助。通用形式为: if/switch
if( init-statement(x); condition(x)) {
// do some stuff here
} else {
// else has the scope of x
// do some other stuff
}
在constexpr的编译时完成
constexpr
很酷!
假设您有一些要评估的表达式,并且初始化后其值不会改变。您可以预先计算该值,然后将其用作宏。或者,如提供的C ++ 11一样,您可以使用 。 constexpr
程序员倾向于尽可能减少其程序的运行时间。因此,如果有一些操作可以使编译器完成并减轻运行时的负担,则可以改善运行时。
上面的代码是的非常常见的示例 。 constexpr
由于我们将fibonacci计算函数声明为 ,因此编译器可以 在编译时进行预计算 。所以编译后就可以替换行了 constexpr
fib(20)
const long long bigval = fib(20);
和
const long long bigval = 2432902008176640000;
请注意,传递的参数是一个 值。这是声明函数的重要点 -传递的参数也应该为 or 。否则,该函数将作为普通函数运行,这意味着在编译期间不会进行任何预先计算。 const
constexpr
constexpr
const
变量也可以是 。如您所料,在那种情况下,这些变量必须在编译时可计算。否则,您将收到编译错误。 constexpr
有趣的是,后来在C ++ 17, 并 进行了介绍。 constexpr-if
constexpr-lambda
元组
就像一样 , 是各种数据类型的固定大小值的集合。 pair
tuple
有时使用 代替 会更方便 。 与普通的C类型数组相似,并具有C ++标准库的几个功能。此数据结构是在C ++ 11中添加的。 std::array
tuple
array
类模板参数推导
功能的非常冗长的名称。这个想法是,从C ++ 17开始,标准类模板也将进行模板的参数推导。以前,仅功能模板支持该功能。
因此,
std::pair<std::string, int> user = {"M", 25}; // previous
std::pair user = {"M", 25}; // C++17
推导的类型是隐式完成的。这对于变得更加方便 。 tuple
// previous
std::tuple<std::string, std::string, int> user ("M", "Chy", 25);
// deduction in action!
std::tuple user2("M", "Chy", 25);
如果您不太熟悉C ++模板,则上述功能将毫无意义。
智能指针
指针可能是地狱般的。
由于C ++之类的语言为程序员提供的自由度,有时候使自己陷入困境非常容易。在很多情况下,指针是造成危害的原因。
幸运的是,C ++ 11引入了智能指针,这些指针比原始指针要方便得多。它们通过尽可能释放内存来帮助程序员防止内存泄漏。它们还提供异常安全性。
我想在这篇文章中写有关C ++中的智能指针的文章。但是显然,关于它们有很多重要的细节。他们应有自己的职位,我当然愿意在不久的将来写一篇关于他们的文章。
今天就这些。请记住,C ++实际上在该语言的最新版本中添加了许多新功能。如果您有兴趣,应该检查一下。这是现代C ++上的一个很棒的存储库,其字面名称为 Awesome Modern C ++ (https://github.com/rigtorp/awesome-modern-cpp)!
阿迪奥斯!
(原文:https://radiant-brushlands-42789.herokuapp.com/medium.com/free-code-camp/some-awesome-modern-c-features-that-every-developer-should-know-5e3bf6f79a3c)
----------------------------
在C ++ 11中move语义
Move语义是右值引用的第一个用例, 其主要目的 是将昂贵的复制操作替换为较便宜的移动操作,例如复制构造函数/复制赋值运算符可以帮助我们实现复制语义,移动构造函数/移动赋值运算符可以帮助我们实现移动语义,但是要了解此主题,我们首先需要了解左值,右值,左值引用和右值引用,然后我们才能讨论这一点。
左值和右值:
左值:通常是表达式,您可以使用例如命名变量的地址
Rvalues: 是不是Lvalues的那些表达式。他们大多是临时人员,其范围仅限于该陈述
例子:
1. int x = 9;// x is lvalue and 9 is rvalue
2. auto emp = getEmployee(101); //emp is lvalue & getEmployee(101) expression and 101 is rvalue
3. processEmployee(emp);//passing lvaue to processEmployee
4. processEmployee(getEmployee(101));//passing rvalue to processEmployee
5. Employee& lemp = emp;//lemp is lvalue reference to lvalue emp
6. Employee&& remp = getEmployee(101); //remp is rvalue reference to rvalue getEmployee(101)
7. const Employee& cemp = getEmployee(101); //const reference to temporary(C++03)/rvalue(C++11)
所以现在我们很清楚Lvalues和Rvalues了,正如在示例5和6中看到的那样,C ++ 11中的lvalue引用和rvalue引用现在有两种类型的引用,以前我们只有一种引用类型,对于临时类型,我们需要使用const参考。
右值引用:正如我们在示例中看到的那样,它们在语法上是用两个&&编写的,顾名思义,它们仅绑定到右值,并且其主要目标是识别可从其移出的对象。
int y = 10;int && x = 9;// fine as rvalue reference can bind to rvalues
int && r = y; //error as you cannot bind lvalue to rvalue reference
这里需要注意的一点是x和r本身是左值,其类型是右值引用,并且它们是左值,因为它们具有名称(x和r),其次我们可以获取它们的地址,并且它们的寿命也超出此行。所以在这之后,如果我们做int && temp = r; 那么这将是错误的,因为r是不能由rvalue reference引用的左值。
移动语义:
现在掌握了左值,右值,左值引用(c ++ 98的引用)和右值引用的知识,让我们跳到Move Semantics(在右值引用的帮助下实现),并了解一个例子:
请记住此示例,以更好地理解此博客文章
所以这里的getMyParser是工厂函数,它将基于传递给函数的第一个参数创建OptionParser或FutureParser对象,第二个参数是const引用,以便它可以同时获取左值和右值,然后将其转发给类的构造函数。完美,但是如果xml解析器的工作是解析巨大的输入xml(将它作为字符串传递给工厂函数,然后作为对构造函数的引用传递给我们,我们将使用该类构造函数初始化类std :: string成员函数作为下面的快速参考)是FutureXML班级 :
因此,如果按以下方式调用工厂函数,则一切都很好:
std::string bigFutureXML ("This is test xml..........");
auto xmlParser = getMyParser(1,bigFutureXML);
因为bigFutureXML在这里是左值,即使在函数调用后我们也不想保留它,所以在m_sfutureXML中复制它很有意义,但是如果函数调用如下:
auto xmlParser = getMyParser(1,std::string("This is test xml......."));
这里的第二个参数是右值,将其复制到类成员函数中是不合逻辑的,因为此右值在此行之后就被销毁了。因此,在这里我们不必调用昂贵的std :: string复制构造函数,并且效率低下,并且由于c ++都是关于效率的,所以在新的c ++ 11中,他们引入了移动语义(通过rvalue引用实现),从而可以移动rvalue而不是被复制。作为示例,这是我们如何实现FutureParser rvalue重载构造函数的方法:
现在,如果我们调用工厂函数,如下所示:
auto xmlParser = getMyParser(1,std::string("This is test xml......."));
那么它将调用FutureParser构造函数的右值重载对吗?
不,记住getMyParser,我们在其中调用类构造函数,如新的FutureParser(arg_sInputXML),此时arg_sInputXML是左值,左值未绑定到右值引用,因此它将调用类左值参数化的构造函数,而不是该值,所以我们该怎么办这样就可以调用该类的右值构造函数,答案是将arg_sInputXML从左值转换为右值,这可以通过std :: move实现。
当arg_sInputXML转换为rvalue时,将调用std :: string move构造函数,并将原始arg_sInputXML渲染为无用,而m_sfutureXML将包含该字符串。这种方法不是很好,因为它将使该左值无效,并且在函数调用后使用该值会导致未定义的行为,因此我们要做的是再编写一个仅对右值有效的重载,如下所示:
请记住,std::move不会移动任何东西,它只是将左值转换为右值的静态转换,对于右值类,移动构造函数被调用,效率更高,其效率的原因及其实现方式将在其他部分进行解释博客文章。
(原文:https://medium.com/@kpl.vermani/move-semantics-in-c-11-a0b836a8b4fa)
------------
用C ++的功能方式做到这一点
(原文作者:Sheik Arbaz)
在毕业的日子里,我花了一些时间在C ++上做竞争性编程。但是我从来没有机会在我的办公室项目中使用C ++,而是用函数式编程(FP)语言进行编码。但是两年后,我有机会从事C ++工作。作为一个非常喜欢凉爽的FP风格(使开发人员的工作变得轻松)的函数式编程爱好者,我开始探索在C ++中以FP风格进行编码的方法。我了解到C ++在最新版本中具有惊人的功能。在此博客中,我们将介绍函数式编程的基础知识,以及在C ++中可能实现的哪些部分。
先决条件:
- 一点C ++经验
- 具备任何函数式编程语言或范例的经验
- 支持C ++ 11标准的C ++编译器
让我们开始吧
函数式编程是一种声明式的编程风格。与命令式风格相反,命令式风格只关注“如何解决”,而它的唯一关注点是“解决什么”。它使用表达式而不是语句。
功能编程关键概念
- 用作一流对象
- 纯功能
功能性编程规则
- 不可变的变量:在函数式编程中,您无法在变量初始化后对其进行修改。你就是不行 您可以创建新变量,但不能修改现有变量。
- 无副作用:副作用是状态变化,而不是当前正在执行的功能。修改函数外部定义的变量,打印到控制台,引发异常以及从文件读取数据都是副作用的示例。
- 无状态:一个函数可能具有内部包含临时状态的局部变量,但是该函数不能引用该函数所属的类或对象的任何成员变量。国家鼓励易变性导致副作用。函数式编程不希望您那样做。
这些是FP的一些关键概念,即规则。即使您始终不遵循所有这些规则,您仍然可以从应用程序中的FP创意中受益。无论如何,C ++绝不是要成为严格或纯函数式编程语言。老实说,函数式编程并不是解决每个问题的正确工具(是的,这是我的观点。将来可能会有所改变。观点会随着经验的变化而改变!)。现在,让我们了解FP擅长解决哪些问题。
用作一流对象
在函数式编程世界中,函数是一流的对象。函数与其他任何变量一样被对待。例如,一个函数可以作为参数传递给其他函数,也可以作为值分配给变量。
用作一流对象(pinterest.com)
在C ++中,函数不是一流的对象。我们得到的最接近的是lambda表达式。
auto printText = [](std::string text) { std::cout << text << std::endl; };
上面的代码创建了一个lambda printText
,它接受一个参数text
,将其输出,但不返回任何内容。该[]
支架是用来指定功能关闭。有关lambda的更多信息,请参见此处。
通过查看如何应用不同的组合器(如过滤器,映射,C ++向量上的reduce或任何集合),来了解如何实现FP。让我们采用以下向量:
std::vector<std::string> messages = { "Hello Pal", "How are you?", "I'm still coding in C++" };
for_each
std::for_each(messages.begin(), messages.end(), printText);
前两个参数是集合的开始和结束。然后,我们传递的第三个参数是对每个元素进行操作的一元lambda。
让我们创建一个自定义学生对象的向量,并在其上应用组合器。
class Student
{
public:
string _name;
int _score;
Student(string name, int score)
{
_name = name;
_score = score;
}
void incrementScore() {
_score += 1;
}
string name()
{
return _name;
}
int score()
{
return _score;
}
};
std::vector<Student> students = {Student("Alice", 85), Student("Bob", 62), Student("Charlie", 81), Student("Jack", 90), Student("Jimmy", 40), Student("Sherlock", 67),};
假设您是老师,想打印他们的详细信息。然后,您可以使用for_each来打印其详细信息。
auto printStudentDetails = [](Student student) { std::cout << student.name() << " " << student.score() << std::endl; };
std::for_each(students.begin(), students.end(), printStudentDetails);
在上面的代码中,我们使用了打印lambda来打印每个学生的详细信息。
地图
在C ++中,该功能的等效项map
是transform
。假设,您提出了一个分数不足或无效数据的问题,并想为每个学生加一个分数。然后,您可以使用transform更新每个学生的分数。
auto addOne = [](Student student) { student.incrementScore(); return student; };
std::transform(students.begin(), students.end(), students.begin(), addOne);
您必须提供输入集合的开始,结束指针,输出集合的开始指针和操作。您甚至可以transform
一次执行两个集合。您可以检查出来,这超出了本博客的范围。
筛选
在C ++中,该功能filter
根据用例具有许多等效项。如果您只想获取具有特定属性的元素的副本,则可以使用copy_if
。总之,有喜欢的其他功能remove_if
,find_if
等过。并且每个这样的函数有一个“ not
”候补以及像copy_if_not
,find_if_not
等等。
auto aboveEighty = [](Student student) { return student.score() > 80; };
vector<Student> topStudents = {};
auto it = std::copy_if(students.begin(), students.end(), back_inserter(topStudents), aboveEighty);
std::for_each(topStudents.begin(), topStudents.end(), printStudentDetails);
的语法为copy_if
:copy_if(Iterator inputBegin, Iterator inputEnd, Iterator outputBegin, predicate)
。您必须提供输入集合的开始,结束指针,输出集合的开始指针和操作。我使用了back_inserter_iterator而不是outputBegin,这使我可以将元素推入topStudents向量,而无需初始化向量。
缩小或折叠
reduce
C ++中的功能等效项是accumulate
。假设您有一个带有数字的向量,并且您想将它们全部求和。
vector<int> numbers{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::cout << std::accumulate(numbers.begin(), numbers.end(), 0,
[](int first, int second) { return first + second; });
您必须提供输入集合的开始,结束指针,累加器的初始值和二进制lambda。累加器也可以应用于我们的自定义学生矢量。下面的代码计算教室的平均分数。
auto binaryOp = [](int score, Student student) { return score + student.score(); };
std::cout << std::accumulate(students.begin(), students.end(), 0, binaryOp)/students.size();
纯函数
在以下情况下,函数是纯函数:
- 该功能的执行没有副作用。
- 函数的返回值仅取决于传递给函数的输入参数。
int sum(int a, int b){
return a+b;
}
上面创建的函数是纯函数。许多内置的C ++函数是纯函数,例如min,max,strlen等。您可以编写接受所有const参数且不更改实例变量的函数。在GCC中,您甚至可以使用“ pure
”属性将函数标记为纯函数,从而实现更好的优化。如果编译器将某个函数称为纯函数,则可以对其进行循环优化(http://en.wikipedia.org/wiki/Loop_optimization)和子表达式消除(http://en.wikipedia.org/wiki/Common_subexpression_elimination)。但是C ++默认情况下也具有副作用,可变性。
public class NonPureFunctionAndMutatingParametersExample{
private int total= 0;
public int add(int nextValue) {
this.total+= nextValue;
return this.total;
}
public void incrementScore(Student &student){
student.score += 1;
}
}
在上面的代码中:
- 添加方法正在改变状态
- IncrementScore方法正在更改函数参数
除非必要,否则我们应避免做上述事情。是的,在某些情况下,副作用和可变性会受益。
不可变变量规则是遵循的好习惯。一旦创建了变量并设置了它的值,就可以完全放心地知道该变量的值将永远不会改变,但有时并不适用。如果要构建必须在最终用户配置较低的计算机上运行的应用程序,则内存和时间将有限。在这种情况下,建议接受参数的引用/指针,对其进行更改以节省内存和复制时间。
没有副作用的规则非常有帮助。它使人确信此功能不会影响外界,因此可以在任何地方调用它。但这在某些情况下(例如,写入数据库)会带来困难(这是副作用)。
还有其他一些概念,规则,例如高阶函数,循环递归等,可以在需要时应用。
结论
正如我之前说的,函数式编程是一个了不起的范例,但是在某些情况下,不遵守其中的一些规则可以提供最佳的解决方案。
在过去的几年中,C ++发生了很大的变化。新的构造使其成为构建任何类型的应用程序的绝佳工具。不再只是 带有Class的C了。快乐编码!!!
(原文:https://medium.com/swlh/doing-it-the-functional-way-in-c-5c392bbdd46a)
------------------------
Lambdas:现代C ++的函数式编程伴侣
(原文:Daksh)
Lambda是C ++ 11的全新功能之一,它改变了我们用C ++编写代码的方式。没有使用lambda的任何现代C ++代码都是不完整的,该lambda允许创建就地匿名函数并将C ++转换为成熟的函数式编程语言。
由于语法和用法相对较新,因此许多C ++程序员仍然发现编写和使用情况有点困难。
本博客旨在描述lambda的语法,用法和适用性,以及它如何帮助编写简短,可维护,功能强大的现代C ++代码。
但是在了解什么是lambda并使用它们之前,我们需要对函数式编程有所了解。
什么是函数式编程
在一个nutshell,函数式编程是所有关于写作类似于数学函数的程序功能。例如,考虑数学函数
f(x)2 = x * x
该函数返回的平方,x
并且是一个独立的函数,既不依赖于函数范围,也不影响函数范围之外的任何事物。给定输入x
,输出将始终是x * x
始终的。
我们还要考虑多项式数学函数,例如(x + y)2(整个平方),写为
f(x,y)2 = x2 + y2 + 2xy
该功能可以简单地分解为多个较小的功能,例如
f(x,y)2 = f(x)2 + f(y)2 + f(2xy)
同样,此处的功能输出仅取决于输入参数。对于给定的输入,该函数将始终生成相同的输出。
函数的其他特征是参数中传递的参数是不可变的。在以上函数f(x)2
和中f(x,y)2
,参数x and y
都是不可变的。它们用于计算,但不会更改,因为数学函数总是从现有值中生成新值。这就是数学的原因,我们看不到像
f(x) = x++
但是x++
在C ++和其他编程语言中普遍存在的功能。函数式编程希望我们编写像数学函数这样的代码,其中函数是
- 既不依赖也不更改功能外部的任何内容。
- 一切都是不可变的,如果我们更改某些内容,它将创建一个新的内容,而不是更改现有项目
为什么要使用函数式编程?
在程序并行运行的多核CPU的世界中,功能性程序可以安全地执行多个CPU,而不必担心过度锁定,因为它不会使用或修改任何全局资源。
功能编程并不是什么新鲜事物,只是随着多核CPU的到来,它的真正潜力得以展现。
Lambda的语法
拉姆达的语法组成的[]
,()
和{}
。下面的代码是有效的lambda语法,应由编译器成功编译
[](){};
在此代码中
-[]
是捕获列表
-()
是函数参数提供程序
-{}
是lambda实现
我们可以创建一个非常简单的lambda,它只将hello world打印为
[](){
cout<<”Hello World”<<endl;
};
但是,lambda无法自动调用自身,我们需要显式调用它们。我们可以像调用其他任何普通函数一样,就地调用lambda。
[](){
cout<<”Hello World”<<endl;
}(); // See Here.. we're calling by appending()调用
Lambda作为函数也可以返回值,并且Lambda的返回类型使用以下->
语法表示:
[]() -> int{
cout<<”Hello World”<<endl;
return 1;
}();
我们可以在任何变量中收集lambda的返回值,如下所示:
int retLambda = []()->int{
cout<<"Hello World"<<endl;
return 1;
}();
就像其他普通函数一样,我们也可以在lambda函数中的参数()中传递为
int retLambda = [](int value)->int{
cout<<"Hello World"<<endl;
return value + 1;
}(100);
lambda函数不必就地调用,我们可以很好地将lambda保留在功能指针内,并在我们选择的位置调用它。
我们可以使用C ++auto
关键字,也可以使用std::function<>
模板来保存函数,我们也可以使用C
样式函数指针int(*lambdaFn)(int)
,但是通常由于语法复杂(读取指针)而避免使用。例如,让我们使用std::function<>
获取函数参考
// Getting the function reference using std::function<>
std::function<int(int)> lambdaFn = [](int value)->int{
cout<<"Hello World"<<endl;
return value + 1;
};// Calling the lambda function here
int retLambda = lambdaFn(100);
lambda函数就像数学函数一样,在将101
值100
传递给lambda函数时,它将始终返回。
我们也可以auto
在取功能参考时使用代替
auto lambdaFn = [](int value)->int{
cout<<"Hello World"<<endl;
return value + 1;
};
为f(x)2
和创建Lambdaf(x,y)2
对于该函数,f(x) = x * x
我们可以将lambda函数编写为
std::function<int(int)> fxsquare = [](int x) ->int {
return x * x;
};
int retValue = fxsquare(10);
对于函数f(x,y)2 = x2 + y2 + 2xy
,我们可以将lambda编写为
std::function<int(int, int)> fxsquare = [](int x, int y) ->int {
int xsquare = [](int x) { return x * x; }(x);
int ysquare = [](int y) { return y * y; }(y);
int twoxy = [](int x, int y) { return 2 * x * y;}(x,y);
return xsquare + ysquare + twoxy;
};
int retValue = fxsquare(5,3);
从这些lambda可以看出,它既满足函数编程的目的,即既不依赖函数,也不更改函数外部的任何内容,以及将输入变量视为不可变的。
我们在这里编写了一个纯函数式代码
除了功能之外,此代码还有什么好处?
在这里,我们不会在全局范围内创建将永远存在的多个功能。内部的所有lambda函数在外部均不可用,并且在函数执行后将不在范围之内。
同时,这些lambda确保我们不会影响作用域变量,因为即使lambda在函数内部就地创建,它也将无法访问在作用域内声明的任何局部变量。例如,在下面的代码中,localvar
无法在lambda函数内部访问该变量
int localvar = 100;
[](){
localvar++; // Compiler error..Not Accessible
}();
lambda的捕获列表[]
捕获列表[]
用于使本地范围变量可用于lambda函数,而无需在参数实参列表中显式传递它们。
可以通过值(即副本)或引用来使局部作用域变量可用。要传递值,我们需要在捕获列表中指定局部作用域变量。如下所示,只有捕获列表中提到的变量才可用于lambda函数。
int localvar = 100;
int localvar2 = 200;
[localvar](){
cout<<localvar; // Success..
cout<<localvar2; // Compiler Error... can't access
}();
要访问所有元素,我们可以在捕获列表中指定所有元素,例如
int localvar = 100;
int localvar2 = 200;
[localvar, localvar2](){ ... }
或者我们可以=
在捕获列表中使用,这意味着所有局部作用域变量都在lambda函数中可用
int localvar = 100;
int localvar2 = 200;
[=](){
cout<<localvar; // Success...
cout<<localvar2; // Success...
}();
按值传递参数是不可变的,即我们无法更改它们的值
int localvar = 100;
int localvar2 = 200;
[=](){
localvar++; // Compiler Error
localvar2++; // Compiler Error
}();
您可能已经理解,这是在lambda中完成的,以实现功能编程的不变性,但是我们仍然可以使用C ++引用&
机制通过引用传递局部范围变量并更改值。这些变化应在全球范围内反映出来。
int localvar = 100;
int localvar2 = 200;
[&](){
localvar++; // Success...
localvar2++; // Success...
}();
为创建Lambda f(x,y)2 using Capture List
我们可以使用捕获列表机制来实现lambda函数,而无需将任何参数传递为
int x = 5, y = 3;
std::function<int(void)> fxsquare = [x,y]() ->int {
int xsquare = [](int x) { return x * x; }(x);
int ysquare = [](int y) { return y * y; }(y);
int twoxy = [](int x, int y) { return 2 * x * y;}(x,y);
return xsquare + ysquare + twoxy;
};
int retValue = fxsquare();
在班级内使用捕获列表
捕获列表也可以在类内部使用,但是,在类函数中,仅this
可用,因此我们只能this
在捕获列表中传递以访问lambdas内部的类变量。
template<int iValue, int jValue>
struct AbcTest {
int i = iValue;
int j = jValue;
int sumFn() {
return [this](){
return this->i + this->j;
}();
}
};
// Using class
AbcTest<100,200> aTest;
int sum = aTest.sumFn()
这就是关于C ++ lambda的全部内容。希望它有助于人们理解并鼓励他们在代码中使用lambda
谢谢阅读…。!!!!
达克什
(原文:https://medium.com/@DakshHub/lambdas-the-companion-of-modern-c-b7dfd43b5abb)
- 点赞
- 收藏
- 关注作者
评论(0)