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

相關文章
相關標籤/搜索