1. 程式人生 > 其它 >C++ lambda表示式

C++ lambda表示式

目錄

lambda表示式簡介

lambda表示式是C++11新特性(C++11 特性),用於建立一個可呼叫單元,可理解成匿名行內函數。

可呼叫單元 是指什麼?
可呼叫單元通常是指可呼叫物件(或函式)。

可呼叫物件

一個物件或表示式,如果可對其使用可呼叫運算子("()"),則稱這個物件或表示式為可呼叫物件。

可呼叫物件組成
4種:函式, 函式指標, 過載了"()"的類,lambda表示式。
本文主要介紹lambda表示式。

[======]

lambda表示式語法

格式

[捕獲列表](引數列表) mutable或exception -> 返回值型別 { 函式體 }

捕獲列表

捕獲列表可以為空,但"[]"不可用省略,用來標識一個lambda表示式開始。
lambda要訪問所在函式的區域性變數時,必須先捕獲。2種區域性變數捕獲方式:

  • 值捕獲:建立lambda表示式時,拷貝所在函式的區域性變數的值。
  • 引用捕獲:建立lambda表示式時,獲取變數的引用。需要確保lambda使用變數時,變數對應物件存在。

注意:值捕獲是發生在lambda建立時,而非呼叫時。

// 值捕獲拷貝變數,發生在lambda建立時
void func1()
{
	size_t v1 = 10;
	auto a = [v1]()->int { return v1; }; // 值捕獲v1 = 10
	v1 = 0;
	auto b = a(); // lambda呼叫時,不會改變捕獲值,因此b為10
}

// 引用捕獲獲取變數引用
void func2()
{
	size_t v1 = 10;
	auto a = [&v1]()->int { return v1; }; // 引用捕獲v1
	v1 = 0;
	auto b = a(); // b為0
}

隱式捕獲

捕獲列表中寫“&”,告訴編譯器採用引用捕獲方式;寫“=”,告訴編譯器採用值捕獲方式。如果有個別局部變數不需要用統一的捕獲方式,可以專門指出其捕獲方式,然後用逗號方式分隔不同捕獲。

string v1 = "abc";
string v2 = "123465";
string v3 = "qwert";

auto f1 = [&](const string& s)->{ cout << v1.size() + s.size() << endl; }; // 引用捕獲
auto f2 = [=](const string& s)->{ cout << v2.size() + s.size() << endl; }; // 值捕獲
auto f3 = [=, &v1](const string& s)->{ cout << v2.size() + s.size() << endl; }; // 引用捕獲v1, 其餘都是值捕獲
auto f4 = [&, v2, v3](const string& s)->{ cout << v2.size() + s.size() << endl; }; // 值捕獲v2, v3, 其餘都是引用捕獲
f1("test1");
f2("test2");
f3("test3");

引數列表

引數列表 可以為空,但"()"不可用省略。類似於函式定義。

mutable關鍵字

  • mutable對於const函式作用
    const成員函式中,通常不能修改non-static資料成員。如果要修改,需要將資料成員宣告為mutable,表示該變數可變,不再有constness(常量性)約束。
    具體可參見Effective C++ 條款03:儘可能使用const

  • mutable對於lambda作用
    在lambda表示式中,mutable有類似效果,預設不能修改值捕獲的變數。
    當lambda要修改值捕獲的變數時,必須新增mutable宣告。

void fcn()
{
	size_t v1 = 10;
	auto f = [v1]() mutable { return ++v1; }; // 加上mutable才能修改值捕獲的v1
	// <=> auto f = [v1]() mutable -> int { return ++v1; }; 
	v1 = 0;
	auto j = f(); // j 為11
}

指定lambda返回型別

預設情況下,
1)如果lambda函式體只包含單一return語句,可以省略lambda返回型別(編譯器自動推斷返回型別)。
2)如果包含return之外的任何語句,編譯器假定此lambda返回void。

簡單來說,如果編譯器無法推斷lambda返回型別,就需要尾置返回型別,不可省略。

例,

vector<int> vec;
... // vec插入資料
	
// OK, 單一return語句, 編譯器能推斷出lambda的返回型別
transform(vec.begin(), vec.end(), vec.begin(), [](int i) { return i < 0 ? -i : i; }); 

// 錯誤, 不能推斷lambda的返回型別
transform(vec.begin(), vec.end(), vec.begin(), [](int i) -> int { 
	/* 非單一return語句 */
	if (i < 0) return -i; 
	else return i;
});