C++11 — lambda表达式(匿名函数)

  C++11中lambda表达式的基本语法格式为:ios

[capture](parameters) -> return_type { /* ... */ }

  其中 [] 内为外部变量的传递方式:函数

[]        //no variables defined. Attempting to use any external variables in the lambda is an error.
[x, &y]   //x is captured by value, y is captured by reference
[&]       //any external variable is implicitly captured by reference if used
[=]       //any external variable is implicitly captured by value if used
[&, x]    //x is explicitly captured by value. Other variables will be captured by reference
[=, &z]   //z is explicitly captured by reference. Other variables will be captured by value

  () 内为参数,好比:测试

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

  return_type就是字面上的意思,也就是返回类型或者说函数类型。{}内则是咱们编写的函数要执行的功能代码。ui

  咱们知道,要执行函数则须要调用函数名,但匿名函数没有函数名(匿名函数最大的好处就是之后定义函数不用再纠结给函数命什么名了),于是忽然间拿到一个lambda表达式,咱们会忽然间有点迷惑,不知道该怎么样执行它。spa

  下面代码则给了几个调用并执行匿名函数的方式:线程

#include <iostream>
#include <vector>
#include <string>
#include <functional> /** function */
#include <algorithm> /** for_each */

/** 创建一个相似建立线程函数的用法的函数来完成执行匿名函数 */
int my_eval(std::function <int(int, int)> f, int x, int y) {
	return f(x, y);
}

int main() {
	/**用法1: 经过将匿名函数存储在变量中,并做为参数传递*/
	std::function<int(int, int)> f0 = [](int x, int y) -> int {return x + y; };//lambda 表达式
	auto                         f1 = [](int x, int y) -> int {return x * y; };//没有使用任何外部变量
	std::cout << "f0: " << my_eval(f0, 3, 4) << std::endl;
	std::cout << "f1: " << my_eval(f1, 2, 3) << std::endl;

	/**用法2: 保存到vector中*/
	std::vector<decltype(f0)> func_s{ f0, f1 };
	func_s.push_back([](int x, int y) {return x - y; });
	for (auto f : func_s)//遍历匿名函数
		std::cout << "f: " << f(3, 1) << std::endl;

	/**用法3: 使用for_each传入匿名函数*/
	std::vector<int> nums{ 1,2,3,4,5 };
	int sum = 0;

	//代码中使用了sum 于是经过[&]引用隐式捕获,若是代码没有使用任何外部变量,则不传递参数
	std::for_each(begin(nums), end(nums), [&](int x) {sum += x; });
	//x为遍历nums传递的值
	std::cout << "f3: " << sum << std::endl;

	//同上用法 但匿名函数参数有不一样的地方
	std::string str{ "hello world" };
	std::string s = "";
	std::string c = " ";

	//指明外部变量s经过引用捕获,c经过传递值捕获
	std::for_each(begin(str), end(str), [&s, c](char x) {s += (x + c); });

	std::cout << "f4: " << s << std::endl;

	/**用法4: 保存到函数指针*/
	auto my_lambda_func = [](int x) {std::cout << "f5: " << x << std::endl;};
	void(*func_ptr)(int) = my_lambda_func;//注意变量名不能与函数指针名冲突
	func_ptr(4);//回调
        /**或者直接这样写:
        * void(*func_ptr)(int) = [](int x) {std::cout << "f5: " << x << std::endl;};
        * func_ptr(4);
        */

        /**用法5: 直接存储在auto类型变量中,而后调用*/
        auto myfunc = [](int x) {std::cout << "f6: " << x << std::endl;};
        myfunc(5);

	return 0;
}

  运行结果:scala

f0: 7
f1: 6
f: 4
f: 3
f: 2
f3: 15
f4: h e l l o  w o r l d
f5: 4
f6: 5

 ------------------update 2018-02-15 02:20:50---------------指针

  前面测试了几个用法,但却不够细节,今天再来补充一些细节。blog

  实际上,代码中有几个值得探究的地方:ip

//从新举两个例子:
auto func = [](std::string params) {return params;};
auto func = [](std::string params) -> std::string {return params;};

  上面两个lambda表达式不一样的地方就是一个有写明返回类型,一个没有。

  而这两种写法均可以正确进行,但前提是必须使用auto类型让编译器本身判断返回类型,不然就会报错:

  使用正确类型变量:

#include <iostream>
#include <string>

int main()
{
    std::string func = [](std::string params) {return params;};
    std::cout << func("hello world!");

    return 0;
}

  报错:

error: conversion from 'main()::<lambda(std::__cxx11::string)>' to non-scalar type 'std::__cxx11::string {aka std::__cxx11::basic_string<char>}' requested|

  使用错误类型变量:

#include <iostream>
#include <string>

int main()
{
    int func = [](std::string params) {return params;};
    std::cout << func("hello world!");

    return 0;
}

  报错:

main.cpp||In function 'int main()':|
main.cpp|6|error: invalid user-defined conversion from 'main()::<lambda(std::__cxx11::string)>' to 'int' [-fpermissive]|
main.cpp|6|note: candidate is: main()::<lambda(std::__cxx11::string)>::operator std::__cxx11::string (*)(std::__cxx11::string)() const <near match>|
main.cpp|6|note:   no known conversion from 'std::__cxx11::string (*)(std::__cxx11::string) {aka std::__cxx11::basic_string<char> (*)(std::__cxx11::basic_string<char>)}' to 'int'|
main.cpp|7|error: 'func' cannot be used as a function|
||=== Build failed: 2 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

  那若是这样呢:

#include <iostream>
#include <string>

int main()
{
    int func = [](std::string params) -> int {return params;};
    std::cout << func("hello world!");

    return 0;
}

  报错:

main.cpp|6|error: cannot convert 'std::__cxx11::string {aka std::__cxx11::basic_string<char>}' to 'int' in return|
main.cpp||In function 'int main()':|
main.cpp|6|error: invalid user-defined conversion from 'main()::<lambda(std::__cxx11::string)>' to 'int' [-fpermissive]|
main.cpp|6|note: candidate is: main()::<lambda(std::__cxx11::string)>::operator int (*)(std::__cxx11::string)() const <near match>|
main.cpp|6|note:   no known conversion from 'int (*)(std::__cxx11::string) {aka int (*)(std::__cxx11::basic_string<char>)}' to 'int'|
main.cpp|7|error: 'func' cannot be used as a function|
||=== Build failed: 3 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

  这样呢:

#include <iostream>
#include <string>

int main()
{
    std::string func = [](std::string params) -> std::string {return params;};
    std::cout << func("hello world!");

    return 0;
}

  而后会发现居然也报错了:

main.cpp||In function 'int main()':|
main.cpp|6|error: conversion from 'main()::<lambda(std::__cxx11::string)>' to non-scalar type 'std::__cxx11::string {aka std::__cxx11::basic_string<char>}' requested|
main.cpp|7|error: no match for call to '(std::__cxx11::string {aka std::__cxx11::basic_string<char>}) (const char [13])'|
||=== Build failed: 2 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

  说是从<lambda(std::__cxx11::string)>转换为非标量类型??不太明白。

  而后试试这样:

#include <iostream>
#include <string>

int main()
{
    char* func = [](char* params) -> char* {return params;};
    std::cout << func("hello world!");

    return 0;
}

  报错:

main.cpp||In function 'int main()':|
main.cpp|7|error: cannot convert 'main()::<lambda(char*)>' to 'char*' in initialization|
main.cpp|8|error: 'func' cannot be used as a function|
||=== Build failed: 2 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

  就是说不能在初始化过程当中对lambda进行转换。

  而后再试试这样:

#include <iostream>
#include <string>

int main()
{
    std::string (*func)(std::string) = [](std::string params) {return params;};
    std::cout << func("hello world!");

    return 0;
}

  这个代码能正常显示结果,但最开始运行了几回会在显示结果后崩溃...不知道为何,而后后面运行就正常了,但显示完结果后会感受很明显的停顿一下才显示Press any key to continue.

  这样写就能正常显示,且没有停顿:

#include <iostream>
#include <string>

int main()
{
    void(*func)(std::string) = [](std::string params) {std::cout << params;};
    func("hello world!");

    return 0;
}

  emmm...

  从这几个测试能够看出,貌似编译器处理lambda表达式的时候,不能直接将匿名函数赋值给一个具备基本类型的变量,这个过程原理是什么还不太明白。但能够直接赋给一个函数指针,而后调用。

  但须要注意:函数指针必须带有类型,不能用auto,由于程序没法推断回调函数的类型。好比:

#include <iostream>
#include <string>

int main()
{
    auto(*func)(std::string) = [](std::string params) {std::cout << params;};
    func("hello world!");

    return 0;
}

  因此,可见使用auto自动判断类型是一个很简便的写法,但也须要正确使用:

#include <iostream>
#include <string>

int main()
{
    auto func = [](std::string params) {std::cout << params;};
    func("hello world!");

    return 0;
}

  总结一下:

  匿名函数能够不用写返回的类型也就是:

 -> return_type 

  彻底能够省略掉。而后能够存储在正确类型的函数指针或auto类型的变量中进行调用。

--------------------------------------

  参考资料:

    维基百科-C++ 11

相关文章
相关标签/搜索