C++11 Lambda表达式

February 14, 2016

今天学习了C++11引入的Lambda表达式,Lambda在使用上有很多的方便之处。我们可以将之用于函数的回调,操作的封装的等地方。如果我们有Objective-c的经验的话,其实我可以将Lambda表达式和Block进行对比学习,两者有很多的相似之处。

优势

根据对Lambda的了解,发现其用途和函数指针有太多的相似之处,甚至可以理解为功能重叠。那么为什么在C++11中还要引入Lambda表达式呢?

1. 函数指针是类型不安全的

C语言的函数指针就是一个地址变量,在赋值和调用的时候没有任何类型检查。任何一种函数指针都可以转型为其他类型的函数指针, 当转型回来时,与原函数指针相同。必须由开发者自己保证正确的类型。函数指针无法提前在编译器给出错误提示。

2. bind能保证类型安全,但对于绑定变量的内容,需要程序员保证有效性

综合上述原因,引入Lambda表达式还是很有必要的

Lambda表达式

我们将从Lambda表达式的定义,调用,传递几个方面进行阐述

Lambda表达式的定义

Lambda表达式的定义我们可以根据下图给出准确描述 Lambda表达式的定义

  1. 表示要捕获的作用域内的变量
       · 为空时表示不捕获任何变量
       · =表示按值传递的捕获,表示捕获作用域内所有的变量
       · 直接输入变量名,表示指定捕获特定的变量
       · &变量名 表示按引入捕获
  2. 表示传入参数的列表
  3. 表示捕获的变量是否在Lambda表达式中可修改
  4. Lambda表达式抛出的异常
  5. Lambda表达式的返回值
  6. Lambda表达式函数体
Lambda表达式的类型是std::function

Lambda表达式使用

了解了Lambda表达式的定义,我们来看看Lambda表达式的使用

不捕获任何变量的

int main()
{
//定义一个不捕获任何变量,返回值为int的Lambda表达式
auto lambda = []() -> int{
std::cout << "my name is " << "codecooker" << std::endl;
return 1;
};
//调用我们定义的Lambda表达式,和调用不同函数没什么区别
lambda();
return 0;
}

编译运行

g++ lambda.cpp -std=c++11 -o lambda
./lambda
output:my name is codecooker
注意:由于Lambda表达式是C++11的特性,所以我们在编译的时候需要加上编译参数-std=c++11

Lambda表达式基础用法

int main()
{
string name = "Powell";
//按照前面描述的定义规则,我们需要捕获作用域内的name变量,这里我们用=号,表示按照值传递捕获
auto lambda = [=]() -> int{
std::cout << "my name is " << name << std::endl;
return 1;
};
lambda();
return 0;
}
output:my name is Powell

下来我们尝试着修改捕获的变量值,代码如下

auto lambda = [=]() -> int{
name = "codecooker";
std::cout << "my name is " << name << std::endl;
return 1;
};

发现编译报错

candidate function not viable: 'this' argument has type 'const string' 
(aka 'const basic_string<char,char_traits<char>, allocator<char> >'),
but method is not marked const basic_string& operator=(const basic_string& __str);

这时我们的mutable就派上了用场,修改代码如下

auto lambda = [=]() mutable -> int{
name = "codecooker";
std::cout << "my name is " << name << std::endl;
return 1;
};
output:my name is codecooker

然而问题并没有结束,看下述代码

int main()
{
string name = "Powell";
//按照前面描述的定义规则,我们需要捕获作用域内的name变量,这里我们用=号,表示按照值传递捕获
auto lambda = [=]() mutable -> int{
name = "codecooker";
std::cout << "my name is " << name << std::endl;
return 1;
};
lambda();
std::cout << "my origin name is " << name << std::endl;
return 0;
}
output:
my name is codecooker
my origin name is Powell

其实这里即使用了mutable修饰我们的Lambda表达式,在Lambda表达式内修改的也只是name变量的一份copy,如果我们需要在Lambda表达式内修改捕获的变量,我们则需要按照引用捕获,如下

auto lambda = [&]() mutable -> int{
name = "codecooker";
std::cout << "my name is " << name << std::endl;
return 1;
};
output:
my name is codecooker
my origin name is codecooker
注意:由于是按照引用捕获,所以我们用不用mutable修饰结果一样的

下面我们来看怎么给Lambda表达式传递参数

int age = 27;
auto lambda = [&](int age) mutable -> int{
name = "codecooker";
std::cout << "my name is " << name << std::endl;
std::cout << "my age is " << age << std::endl;
return 1;
};
lambda(age);
output:
my name is codecooker
my age is 27

和普通函数传递参数是一样的,大家可以发挥想象力

Lambda表达式作为参数

前面已经说过Lambda表达式是std::function类型的变量,既然是变量那么必须可以作为参数进行传递,有了上边的描述,直接上代码

#include <iostream>
#include <string>
#include <functional>

using std::function;
using std::string;


void foo(function<int(int)> lambda);

int main()
{
string name = "Powell";
int age = 27;
auto lambda = [&](int age) mutable -> int{
name = "codecooker";
std::cout << "my name is " << name << std::endl;
std::cout << "my age is " << age << std::endl;
return 1;
};
foo(lambda);
return 0;
}

void foo(function<int(int age)> lambda) {
int age = 27;
auto result = lambda(age);
std::cout << "result is " << result << std::endl;
}
output:
my name is codecooker
my age is 27
result is 1

至此,关于Lambda表达式的相关内容相信大家已经了然于胸了。

Comments