程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 聯合C++11新特征來進修C++中lambda表達式的用法

聯合C++11新特征來進修C++中lambda表達式的用法

編輯:關於C++

聯合C++11新特征來進修C++中lambda表達式的用法。本站提示廣大學習愛好者:(聯合C++11新特征來進修C++中lambda表達式的用法)文章只能為提供參考,不一定能成為您想要的結果。以下是聯合C++11新特征來進修C++中lambda表達式的用法正文


在 C++ 11 中,lambda 表達式(平日稱為 "lambda")是一種在被挪用的地位或作為參數傳遞給函數的地位界說匿名函數對象的輕便辦法。 Lambda 平日用於封裝傳遞給算法或異步辦法的大批代碼行。 本訂婚義了 lambda 是甚麼,將 lambda 與其他編程技巧停止比擬,描寫其長處,並供給一個根本示例。
Lambda 表達式的各部門
ISO C++ 尺度展現了作為第三個參數傳遞給 std::sort() 函數的簡略 lambda:

#include <algorithm>
#include <cmath>

void abssort(float* x, unsigned n) {
 std::sort(x, x + n,
 // Lambda expression begins
 [](float a, float b) {
  return (std::abs(a) < std::abs(b));
 } // end of lambda expression
 );
}

此圖顯示了 lambda 的構成部門:

  1. Capture 子句(在 C++ 標准中也稱為 lambda 引誘。)
  2. 參數列表(可選)。 (也稱為 lambda 聲明符)
  3. 可變標准(可選)。
  4. 異慣例范(可選)。
  5. 尾隨前往類型(可選)。
  6. “lambda 體”
  7. Capture 子句

    Lambda 可在其主體中引入新的變量(用 C++14),它還可以拜訪(或“捕捉”)周邊規模內的變量。 Lambda 以 Capture 子句(尺度語法中的 lambda 引誘)開首,它指定要捕捉的變量和是經由過程值照樣援用停止捕捉。 有與號 (&) 前綴的變量經由過程援用拜訪,沒有該前綴的變量經由過程值拜訪。
    空 capture 子句 [ ] 指導 lambda 表達式的主體不拜訪關閉規模中的變量。
    可使用默許捕捉形式(尺度語法中的 capture-default)來指導若何捕捉 lambda 中援用的任何內部變量:[&] 表現經由過程援用捕捉援用的一切變量,而 [=] 表現經由過程值捕捉它們。 可使用默許捕捉形式,然後為特定變量顯式指定相反的形式。 例如,假如 lambda 體經由過程援用拜訪內部變量 total 並經由過程值拜訪內部變量 factor,則以下 capture 子句等效:

    [&total, factor]
    [factor, &total]
    [&, factor]
    [factor, &]
    [=, &total]
    [&total, =]
    

    應用 capture-default 時,只要 lambda 中說起的變量才會被捕捉。
    假如 capture 子句包括 capture-default&,則該 capture 子句的 identifier 中沒有任何 capture 可采取 & identifier 情勢。 異樣,假如 capture 子句包括 capture-default=,則該 capture 子句的 capture 不克不及采取 = identifier 情勢。 identifier 或 this 在 capture 子句中湧現的次數不克不及跨越一次。 以下代碼片斷給出了一些示例。

    struct S { void f(int i); };
    
    void S::f(int i) {
     [&, i]{}; // OK
     [&, &i]{}; // ERROR: i preceded by & when & is the default
     [=, this]{}; // ERROR: this when = is the default
     [i, i]{}; // ERROR: i repeated
    }
    
    

    capture 後跟省略號是包擴大,如以下可變參數模板示例中所示:

    template<class... Args>
    void f(Args... args) {
     auto x = [args...] { return g(args...); };
     x();
    }
    

    要在類辦法的注釋中應用 lambda 表達式,請將 this 指針傳遞給 Capture 子句,以供給對關閉類的辦法和數據成員的拜訪權限。 有關展現若何將 lambda 表達式與類辦法一路應用的示例,請參閱 Lambda 表達式的示例中的“示例:在辦法中應用 Lambda 表達式”。
    在應用 capture 子句時,建議你記住以下幾點(特別是應用采用多線程的 lambda 時):
    援用捕捉可用於修正內部變量,而值捕捉卻不克不及完成此操作。 (mutable許可修正正本,而不克不及修正原始項。)
    援用捕捉會反應內部變量的更新,而值捕捉卻不會反應。
    援用捕捉引入生計期依附項,而值捕捉卻沒有生計期依附項。 當 lambda 以異步方法運轉時,這一點特別主要。 假如在異步 lambda 中經由過程援用捕捉當地變量,該當地變量將極可能在 lambda 運轉時消逝,從而招致運轉時拜訪抵觸。

    通用捕捉 (C++14)
    在 C++14 中,可在 Capture 子句中引入並初始化新的變量,而無需使這些變量存在於 lambda 函數的關閉規模內。 初始化可以任何隨意率性表達式表現;且將從該表達式生成的類型推導新變量的類型。 此功效的一個利益是,在 C++14 中,可從周邊規模捕捉只挪動的變量(例如 std::unique_ptr)並在 lambda 中應用它們。

    pNums = make_unique<vector<int>>(nums);
    //...
     auto a = [ptr = move(pNums)]()
     {
      // use ptr
     };
    

    參數列表
    除捕捉變量,lambda 還可接收輸出參數。 參數列表(在尺度語法中稱為 lambda 聲明符)是可選的,它在年夜多半方面相似於函數的參數列表。

    int y = [] (int first, int second)
    {
     return first + second;
    };
    

    在 C++14 中,假如參數類型是泛型,則可使用 auto 症結字作為類型解釋符。 這將告訴編譯器將函數挪用運算符創立為模板。 參數列表中的每一個 auto 實例等效於一個分歧的類型參數。

    auto y = [] (auto first, auto second)
    {
     return first + second;
    };
    

    lambda 表達式可以將另外一個 lambda 表達式作為其參數。 有關具體信息,請參閱 Lambda 表達式的示例主題中的“高階 Lambda 表達式”。
    因為參數列表是可選的,是以在不將參數傳遞到 lambda 表達式,而且其 lambda-declarator: 不包括 exception-specification、trailing-return-type 或 mutable 的情形下,可以省略空括號。

    可變標准
    平日,lambda 的函數挪用運算符為 const-by-value,但對 mutable 症結字的應用可將其撤消。 它不會生成可變的數據成員。 應用可變標准,lambda 表達式的主體可以修正經由過程值捕捉的變量。 本文前面的一些示例將顯示若何應用 mutable。
    異慣例范
    你可使用 throw() 異慣例范來指導 lambda 表達式不會激發任何異常。 與通俗函數一樣,假如 lambda 表達式聲明 C4297 異慣例范且 lambda 體激發異常,Visual C++ 編譯器將生成正告 throw(),以下所示:

    // throw_lambda_expression.cpp
    // compile with: /W4 /EHsc 
    int main() // C4297 expected
    {
     []() throw() { throw 5; }();
    }
    

    前往類型
    將主動推導 lambda 表達式的前往類型。 無需應用 auto 症結字,除非指定尾隨前往類型。 trailing-return-type 相似於通俗辦法或函數的前往類型部門。 然則,前往類型必需跟在參數列表的前面,你必需在前往類型後面包括 trailing-return-type 症結字 ->。
    假如 lambda 體僅包括一個前往語句或其表達式不前往值,則可以省略 lambda 表達式的前往類型部門。 假如 lambda 體包括單個前往語句,編譯器將從前往表達式的類型推導前往類型。 不然,編譯器會將前往類型推導為 void。 上面的代碼示例片斷解釋了這一准繩。

    auto x1 = [](int i){ return i; }; // OK: return type is int
    auto x2 = []{ return{ 1, 2 }; }; // ERROR: return type is void, deducing 
         // return type from braced-init-list is not valid
    
    

    lambda 表達式可以生成另外一個 lambda 表達式作為其前往值。 有關具體信息,請參閱 Lambda 表達式的示例中的“高階 Lambda 表達式”。
    Lambda 體
    lambda 表達式的 lambda 體(尺度語法中的 compound-statement)可包括通俗辦法或函數的主體可包括的任何內容。 通俗函數和 lambda 表達式的主體都可拜訪以下變量類型:

    • 從關閉規模捕捉變量,如前所述。
    • 參數
    • 當地聲明變量
    • 類數據成員(在類外部聲明而且捕捉 this 時)
    • 具有靜態存儲連續時光的任何變量(例如,全局變量)

    以下示例包括經由過程值顯式捕捉變量 n 並經由過程援用隱式捕捉變量 m 的 lambda 表達式:

    // captures_lambda_expression.cpp
    // compile with: /W4 /EHsc 
    #include <iostream>
    using namespace std;
    
    int main()
    {
     int m = 0;
     int n = 0;
     [&, n] (int a) mutable { m = ++n + a; }(4);
     cout << m << endl << n << endl;
    }
    
    

    輸入:

      5
    0
    

               
    因為變量 n 是經由過程值捕捉的,是以在挪用 lambda 表達式後,變量的值仍堅持 0 不變。 mutable 標准許可在 lambda 中修正 n。
    雖然 lambda 表達式只能捕捉具有主動存儲連續時光的變量,但你可以在 lambda 表達式的主體中應用具有靜態存儲連續時光的變量。 以下示例應用 generate 函數和 lambda 表達式為 vector 對象中的每一個元素賦值。 lambda 表達式將修正靜態變量以生成下一個元素的值。

    void fillVector(vector<int>& v)
    {
     // A local static variable.
     static int nextValue = 1;
    
     // The lambda expression that appears in the following call to
     // the generate function modifies and uses the local static 
     // variable nextValue.
     generate(v.begin(), v.end(), [] { return nextValue++; }); 
     //WARNING: this is not thread-safe and is shown for illustration only
    }
    
    

    上面的代碼示例應用上一示例中的函數,並添加了應用 STL 算法 generate_n 的 lambda 表達式的示例。 該 lambda 表達式將 vector 對象的元素指派給前兩個元素之和。 應用了 mutable 症結字,以使 lambda 表達式的主體可以修正 lambda 表達式經由過程值捕捉的內部變量 x 和 y 的正本。 因為 lambda 表達式經由過程值捕捉原始變量 x 和 y,是以它們的值在 lambda 履行後仍為 1。

    // compile with: /W4 /EHsc
    #include <algorithm>
    #include <iostream>
    #include <vector>
    #include <string>
    
    using namespace std;
    
    template <typename C> void print(const string& s, const C& c) {
     cout << s;
    
     for (const auto& e : c) {
     cout << e << " ";
     }
    
     cout << endl;
    }
    
    void fillVector(vector<int>& v)
    {
     // A local static variable.
     static int nextValue = 1;
    
     // The lambda expression that appears in the following call to
     // the generate function modifies and uses the local static 
     // variable nextValue.
     generate(v.begin(), v.end(), [] { return nextValue++; });
     //WARNING: this is not thread-safe and is shown for illustration only
    }
    
    int main()
    {
     // The number of elements in the vector.
     const int elementCount = 9;
    
     // Create a vector object with each element set to 1.
     vector<int> v(elementCount, 1);
    
     // These variables hold the previous two elements of the vector.
     int x = 1;
     int y = 1;
    
     // Sets each element in the vector to the sum of the 
     // previous two elements.
     generate_n(v.begin() + 2,
     elementCount - 2,
     [=]() mutable throw() -> int { // lambda is the 3rd parameter
     // Generate current value.
     int n = x + y;
     // Update previous two values.
     x = y;
     y = n;
     return n;
     });
     print("vector v after call to generate_n() with lambda: ", v);
    
     // Print the local variables x and y.
     // The values of x and y hold their initial values because 
     // they are captured by value.
     cout << "x: " << x << " y: " << y << endl;
    
     // Fill the vector with a sequence of numbers
     fillVector(v);
     print("vector v after 1st call to fillVector(): ", v);
     // Fill the vector with the next sequence of numbers
     fillVector(v);
     print("vector v after 2nd call to fillVector(): ", v);
    }
    
    

    輸入:

      vector v after call to generate_n() with lambda: 1 1 2 3 5 8 13 21 34
    x: 1 y: 1
    vector v after 1st call to fillVector(): 1 2 3 4 5 6 7 8 9
    vector v after 2nd call to fillVector(): 10 11 12 13 14 15 16 17 18
    

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved