1. 程式人生 > >C++lambda詳解~讀書筆記

C++lambda詳解~讀書筆記

lambda表示式:

[capture_block](parameters) mutable exception_specification->return_type{ body }

lambda表示式包含以下部分:

捕捉塊(catpure block): 指定如何捕捉所在作用域中的變數,並供給lambda主體使用。

引數(parameter): (可選)lambda表示式使用的引數列表。只有在不使用任何引數,並且沒有自定mutable、一個exception_specification 和一個return_type的情況下可以忽略該列表,返回型別在某些情況下也是可以忽略的,詳見對return_type的說明:eg: [] {return 10;}

引數列表和普通函式的引數列表類似,區別如下:

引數不能有預設值。

不允許變長引數列表。

不允許未命名的引數。

mutable:(可選)如果所在作用域的變數是通過值捕捉到,那麼lambda表示式主體中可以使用這些變數的副本。這些副本預設標記為const,因此lambda表示式的主體不能修改這些副本的值。如果lambda表示式標記為mutable,那麼這些副本則不是const,因此主體可以修改這些本地副本。

exception_specification:(可選)用於指定lambda可以丟擲的異常。

return_type:(可選)返回值的型別。如果忽略了return_type,那麼編譯器會根據以下原則判斷返回型別:

如果lambda表示式主體的形式為{return expression;}那麼表示式return_type的型別為expression的型別。

其他情況下的return_type為void。

下面的例子演示瞭如何建立一個lambda表示式並立即執行這個表示式。這行程式碼定義了一個沒有返回值也沒有任何引數的lambda表示式。

注意:尾部的(),這對括號使得這個lambda表示式立即執行:

	[] {cout << "Hello from Lambda" << endl; } ();
	string result = [](const string & str) -> string { return "Hello from " + str; }("second Lambda");
	cout << "Result: " << result << endl;
輸出如下:
	Result: Hello from second Lambda

根據前面的描述,這個例子中的返回值可以忽略:

	string result = [](const string & str){ return "Hello from " + str; }("second Lambda");

還可以儲存lambda表示式的指標,並且通過函式指標執行這個lambda表示式。使用C++11的auto關鍵字可以輕鬆地做到這一點:
	auto fn = [](const string& str) {return "hello from " + str; };
	cout << fn("call 1") << endl;
	cout << fn("call 2") << endl;
輸出如下:
	Hello from call 1
	Hello from call 2


捕捉塊

lambda表示式的方括號部分稱為lambda捕捉塊(capture block),在這裡可以指定如何從所在作用域中捕捉變數。捕捉變數的意思是可以在lambda表示式主體中使用這個變數。有兩種方式:

[=]:通過值捕捉所有變數

[&]:通過引用捕捉所有變數

指定空白的捕捉塊[]表示不從所在作用域中捕捉變數。還可以酌情決定捕捉那些變數以及這些變數的捕捉方法,方法是指定一個捕捉列表,其中帶有可選的預設捕捉選項。字首為&的變數通過引用捕捉。不帶字首的變數通過值捕捉。預設捕捉應該是捕捉列表中的第一個元素,可以是=或&。

例如:

[&x]只通過引用捕捉x,不捕捉其他變數。

[x]只通過值捕捉x,不捕捉其他變數。

[=, &x, &y]預設通過值捕捉,變數x和y例外,這兩個變數通過引用捕捉。

[&, x]預設通過引用捕捉,變數x例外,這個變數通過引用捕捉。

[&x, &y]非法,因為標誌符不允許重複。

通過引用捕捉變數的時候,一定保證黨lambda表示式在執行的時候,這個引用還是可用的。

將lambda表示式用作返回值

定義在<functional>標頭檔案中的std::function是多型的函式物件包裝,類似函式指標。它可以繫結至任何可以被呼叫的物件(仿函式、成員函式指標、函式指標和lambda表示式),只要引數和返回型別符合包裝的型別即可。返回一個double、接受兩個整數引數的函式包裝定義如下:

	function<double(int, int)> myWrapper;
通過使用std::function,可從函式中返回lambda表示式,看一下定義:
	function<int(void)> multiplyBy2Lambda(int x)
	{
		return [=]()->int{return 2 * x; };
	}

在這個例子中,lambda表示式的返回型別和空引數列表可以忽略,可改寫為:
	function<int(void)> multiplyBy2Lambda(int x)
	{
		return[=] {return 2 * x; };
	}
這個函式的主體部分建立了一個lambda表示式,這個lambda表示式通過值捕捉所在作用域的變數,並返回一個整數,這個返回的整數是傳給multiplyBy2Lambda()的值的兩倍。這個multiplyBy2Lambda()函式的返回值型別為 function<int(void)>,即一個不接受引數並返回一個整數的函式。函式主體中定義的lambda表示式正好匹配這個原型。變數x通過值捕捉,因此,在lambda表示式從函式返回之前,x值的一份副本繫結至lambda表示式中的x。

可以通過以下方式呼叫上述函式:

	function<int(void)> fn = mutiplyBy2Lambda(5);
	cout << fn() << endl;
通過C++11的auto關鍵字可以簡化這個呼叫:
	auto fn = mutiplyBy2Lambda(5);
	cout << fn() << endl;
輸出為10。

mutiplyBy2Lambda()示例通過值捕捉了變數x:[=]。假設這個函式重寫為通過引用捕捉變數:[&],如下所示。根據程式碼所示。根據程式碼後面的解釋,下面這段程式碼不能正常工作:

	function<int(void)> mutiplyBy2Lambda(int x)
	{
		return[&] {return 2 * x; };
	}

lambda表示式通過引用捕捉變數x。然而,lambda表示式會在程式後面執行,而不會在mutiplyBy2Lambda()函式的作用域中執行,在那裡x的引用不再有效。


將lambda表示式用作引數:
您可以編寫lambda表示式作為引數的函式。例如,可通過這種方式實現回撥函式。下面的程式碼實現了一個testCallback()函式,這個函式接受一個整數vector和一個回撥函式作為引數。這個實現迭代給定vector中的所有元素,並對每個元素呼叫回撥函式,回撥函式接受vector中每個元素作為int引數,並返回一個布林值。如果回撥函式返回false,那麼停止迭代。

void testCallback(const vector<int>& vec, const function<bool(int)>& callback)
{
	for (auto i : vec)
	{
		if (!callback(i))
			break;
		cout << i << " ";
	}
	cout << endl;
}
可以按照以下方式測試testCallback()函式。
	vector<int> vec(10);
	int index = 0;
	generate(vec.begin(), vec.end(), [&index] {return ++index; });
	for each (vec.begin(), vec.end()m[](int i) {cout << u << " "; });
	{
		cout << endl;
		testCallback(vec, [](int i){return i < 6; });
	}

輸出結果:

	1 2 3 4 5 6 7 8 9 10
	1 2 3 4 5