lambda的基础知识及用法,已在另一篇文章中阐明,此处不再赘述。
下面,介绍一些lambda表达式的另一些主要注意的点。
修改值捕获的非静态局部变量
前一篇文章中已说明,想要在lambda表达式的函数体中修改非静态局部变量的值,需要引用捕获。
如果非要修改值捕获的变量,有方法吗?有!加上关键字mutable。
但是!修改对原变量无任何影响!
代码如下:
int _tmain(int argc, _TCHAR* argv[])
{
using namespace std;
string strTemp = "非静态局部变量";
auto fun = [strTemp]()mutable{
strTemp = "local";
};
fun();
cout << strTemp << endl;
system("pause");
return 0;
}
执行结果如下:
结果是不是有些出乎意料?fun函数体中的修改,居然对strTemp本身无任何影响。为何如此?请看下面的点。
值捕获,在lambda定义时变量的值就固定不变;引用捕捉,为lambda调用时变量的值
什么意思呢?先上代码:
int _tmain(int argc, _TCHAR* argv[])
{
using namespace std;
string strTemp = "非静态局部变量";
int iTemp = 10;
//定义lambda
auto fun = [strTemp, &iTemp]()
{
cout << strTemp << endl;
cout << iTemp << endl;
};
//改变两个局部变量的值
strTemp = "local";
iTemp = 20;
fun();//执行lambda
system("pause");
return 0;
}
执行结果如下:
以上代码,fun中,strTemp为值捕获,iTemp为引用捕获。
在fun定义的时候,值捕获的变量的值就固定不变。可以理解为,值捕获创建了一个临时对象,拷贝了原变量的值,之后,这个临时变量与原来的变量再无任何瓜葛,lambda中操作的是这个临时变量。
而引用捕获,创建的是一个引用/指针,指向了原来的变量,lambda函数体中操作的就是变量本身。
在fun定义后,对strTemp的改变,是原变量,不会影响到定义时创建的临时对象。这也就能解释为什么另一篇的代码,在函数体中修改值捕获的变量对原变量没影响,因为这本身就是两个变量。
隐式捕获
前面介绍的,将变量名写入捕获列表,为显式捕获。如果lambda中需要用到的非静态局部变量非常多,全部写入捕获列表,就会显得非常臃肿,有简便写法,即为隐式捕获。
1、捕获列表的形式为"[=]"(值捕获)或者"[&]"(引用捕获);
2、捕获列表里不能同时写"="和"&",即"[=, &]"是非法的;
3、隐式捕获可以和显式捕获搭配使用,但不能和同类型的显示捕获一起使用。即隐式值捕获只能搭配显式引用捕获,隐式引用捕获只能搭配显式值捕获。表现形式为:"[=,&变量1,&变量2]"或者"[&,变量1,,变量2]"。
示例代码如下:
int _tmain(int argc, _TCHAR* argv[])
{
using namespace std;
string strTemp = "非静态局部变量";
int iTemp = 10;
double dValue = 13.14;
bool bOK = true;
auto fun_ref = [&, strTemp, iTemp]() mutable //strTemp和iTemp为值捕获,其他局部变量为引用捕获
{
strTemp = "local"; //显式值捕获
iTemp = 15; //显式值捕获
dValue = 5.20; //隐式引用捕获
bOK = false; //隐式引用捕获
};
fun_ref();
cout << strTemp << endl;
cout << iTemp << endl;
cout << dValue << endl;
cout << boolalpha << bOK << endl;
auto fun_value = [=, &strTemp, &iTemp]() mutable //strTemp和iTemp为引用捕获,其他局部变量为值捕获
{
strTemp = "local"; //显式引用捕获
iTemp = 15; //显式引用捕获
dValue = 12.12; //隐式值捕获
bOK = true; //隐式值捕获
};
cout << "------------------------------------------" << endl;
fun_value();
cout << strTemp << endl;
cout << iTemp << endl;
cout << dValue << endl;
cout << boolalpha << bOK << endl;
//auto fun_ = [=, strTemp, iTemp](){}; //这种定义是非法的,=表示隐式值捕获,显式捕获只能采用引用捕获的方式
system("pause");
return 0;
}
执行结果如下:
这段代码,fun_ref中显式“值捕获”了strTemp、iTemp两个变量,隐式“引用捕获”了其他变量。在fun_ref中修改值捕获的变量strTemp和iTemp,不会改变原变量的值;而对dValue和bOK的修改,会影响到原变量。
fun_value中则恰好相反,变量strTemp和iTemp为引用捕获,函数体中的修改会作用于原变量。其他变量为值捕获,修改不会导致原变量修改。
至此,lambda的绝大多数知识点已结束,但还留了一个东西:返回值。
返回值
在《C++ Primer》第五版中,有这么一句话:
翻译过来就是:如果lambda的函数体包含任何多于单一return语句的内容,且未指定返回类型,则返回void。
这句话很让人费解,再结合后面给出的示例:
从这些描述中,不难看出,作者想表达的意思是,lambda的函数体中只能有单一的return语句,否则就应该显式指定返回类型。如果函数体中不是单一的return语句,又没有显式指定返回类型,则会返回void。
但是,实际测试下来,并不是这么回事。
上述代码在visual studio2013中可以正常编译通过并运行:
int _tmain(int argc, _TCHAR* argv[])
{
using namespace std;
vector<int> vi = { -10, 2, -3, 14, -5 };
transform(vi.begin(), vi.end(), vi.begin(),
[](int i) {
if (i < 0)
return -i;
else
return i;
});
for (int i : vi)
cout << i << " ";
cout << endl;
system("pause");
return 0;
}
执行结果如下:
难道是编译器的问题?在“https://en.cppreference.com/”提供的在线编译器中选择gcc,编译运行,也是可以的:
至于C++的文档,在https://en.cppreference.com里面有关lambda的介绍中:https://en.cppreference.com/w/cpp/language/lambda ,也没有看到相关的描述。
相当困惑,《C++ Primer》的这句话,究竟是他使用的编译器的限制,还是C++标准的限制。
如果哪位同仁找到依据,请不吝赐教!
在visual studio2013编译器中,如果指定了lambda的返回值类型,则只需要函数体中return的类型可以隐式转换成指定的返回值类型,编译就可以通过编译,类型不匹配,可能会给出警告,代码如下:
int _tmain(int argc, _TCHAR* argv[])
{
using namespace std;
auto fun = [](int i)->int
{
if (i % 2 == 0)
return true;
else
return 11.14;
};
cout << boolalpha << fun(3) << endl;;
system("pause");
return 0;
}
编译后会有一句警告:
这里是从11.14转换成int会丢失精度的警告,至于reurn true,会直接将bool类型转换成int类型,执行结果如下:
如果没有显式指定返回值,编译器会以第一个return语句的类型作为返回值类型,之后的所有return语句,必须是此类型,否则会直接报错。
这一条,比显式指定返回值更严格,将上面的代码去掉返回类型,如下:
int _tmain(int argc, _TCHAR* argv[])
{
using namespace std;
auto fun = [](int i) //不再指定返回值类型
{
if (i % 2 == 0)
return true;
else
return 11.14;
};
cout << boolalpha << fun(3) << endl;;
system("pause");
return 0;
}
编译结果:
有关lambda表达式,需要在实践中不断尝试。有关《C++ Primer》中返回值的部分,如果有哪位同仁知道出处,麻烦一定要告诉我一下!!!感激涕零!!!