1. 程式人生 > >C++ Primer 筆記——lambda表達式

C++ Primer 筆記——lambda表達式

編譯 test cal lac img prime 混合 parameter 技術

1.一個lambda表達式表示一個可調用的代碼單元,可以理解為一個未命名的內聯函數,但是與函數不同,lambda表達式可能定義在函數內部。其形式如下:

[capture list] (parameter list) -> return type { function body }

  • capture list 是一個lambda所在函數中定義的局部變量的列表(通常為空)
  • return type, parameter list 和 function body與任何普通函數一樣,分別表示返回類型,參數列表和函數體
  • lambda必須使用尾置返回
  • 我們可以忽略參數列表和返回類型,但必須永遠包含捕獲列表和函數體
  • 如果忽略返回類型,lambda會從函數體推斷出返回類型。
  • 如果lambda的函數體包含任何單一return語句之外的內容,且未指定返回類型,則返回void
  • 捕獲列表只用於局部非static變量,lambda可以直接使用局部static變量和在它所在函數之外聲明的名字。
void test()
{
    int i = 1;
    int j = 2;
    auto f = [i, j](int base) -> int { return base + i + j; };    // i,j必須在捕獲列表裏面,這裏的返回類型int其實可以省略
    int
k = f(0); // k的結果為3 }

2.當定義一個lambda時,編譯器生成一個與lambda對應的新的(未命名的)類類型。默認情況下這個類都包含一個對於該lambda所捕獲的變量的數據成員。類似任何普通類的數據成員,lambda的數據成員也在lambda對象創建時被初始化。

3.與傳值參數類似,采用值捕獲的前提是變量可以拷貝,與參數不同,被捕獲的變量的值是在lambda創建時拷貝,而不是調用時拷貝。

void test()
{
    int i = 10;
    auto f = [i] {return i + 1; };    // 此時i的值就已經被拷貝了
int j = f(); }

4.當以引用方式捕獲一個變量時,必須保證在lambda執行時變量是存在的。

void test()
{
    int i = 10;
    auto f = [&i] {return ++i; };
    int j = f();    // 此時i等於11
}

5.在捕獲列表中寫一個 &或= 可以告訴編譯器我們想采用值捕獲還是引用捕獲,這種方法叫做隱式捕獲。

void test()
{
    int i = 10;
    auto f = [&] {return ++i; };    // 采用引用捕獲
    int j = f();    
}


6.我們也可以混合使用值捕獲或者引用捕獲。當使用混合方式的時候,顯示捕獲的方式不可以與隱式捕獲的方式相同。

void test()
{
    int i = 10;
    int j = 9;
    auto f = [=, &i] {i++; return i + j; };    // i采用引用捕獲,其他采用值捕獲
    int k = f();    // k等於20
}

技術分享

7.默認情況下,對於一個值被拷貝的變量,lambda不會改變其值,如果我們希望能改變一個被捕獲的變量的值,就必須在參數列表首加上mutable。

void test()
{
    int i = 10;
    auto f = [i] (){return ++i; };    // 錯誤,不可以改變i的值
    auto f1 = [i] () mutable {return ++i; };    // 正確
}

8.bind函數(頭文件 functional中)可以看作一個通用的函數適配器,它接受一個可調用對象,生成一個新的可調用對象來適應原對象的參數列表。調用bind的一般形式為:

auto newCallable = bind(callable, arg);

void test(int i, int j)
{
    std::cout << i - j << std::endl;
}

auto test0 = std::bind(test, 5, 1);
test0();    // 結果4

auto test1 = std::bind(test, std::placeholders::_1, 5, std::placeholders::_2, 1);    // 錯誤,不可以這麽寫
test1();    

auto test2 = std::bind(test, std::placeholders::_1, 5);
test2(1);    // 結果-4

auto test3 = std::bind(test, std::placeholders::_1, std::placeholders::_2);
test3(3, 2);    // 結果1

auto test4 = std::bind(test, std::placeholders::_2, std::placeholders::_1);    // 參數順序可以重排
test4(3, 2);    // 結果-1

auto test5 = std::bind(test, 1, std::placeholders::_1);        // 註意這個占位符占的是test5()的參數位置,而不是test()的
test5(5);    // 結果-4

以上代碼請註意命名空間,否則會和socket的bind函數產生二義性。

9.如果我們想綁定的參數無法拷貝,或者我們想引用時應該使用ref函數。

void test(int& i, int j)
{
    std::cout << i - j << std::endl;
}

int i = 5;
auto test0 = std::bind(test, std::ref(i), 1);
test0();    // 結果4


C++ Primer 筆記——lambda表達式