閱讀358 返回首頁    go 阿裏雲 go 技術社區[雲棲]


C++閉包: Lambda Functions in C++11

a lambda表達式無疑是C++11最激動人心的特性之一!它會使你編寫的代碼變得更優雅、更快速! 它實現了C++11對於支持閉包的支持。首先我們先看一下什麼叫做閉包

維基百科上,對於閉包的解釋是:

In programming languages, a closure (also lexical closure orfunction closure) is afunction or reference to a function together with a referencing environment—a table storing areference to each of the non-local variables (also called free variables or upvalues) of that function.[1] A closure—unlike a plainfunction pointer—allows a function to access those non-local variables even when invoked outside its immediatelexical scope.

The concept of closures was developed in the 1960s and was first fully implemented in 1975[citation needed] as a language feature in the Scheme programming language to support lexically scoped first-class functions. The use of closures is associated with functional programming languages such as Lisp and ML, as traditional imperative languages such as Algol, C and Pascal do not support returning nested functions as results from higher-order functions and thus do not require supporting closures either. Many moderngarbage-collected imperative languages support closures, such as Smalltalk (the first object-oriented language to do so)[2],JavaScript and C#.

簡單來說,閉包Closure)是詞法閉包Lexical Closure)或者函數閉包的簡稱,是引用了自由變量的函數。這個被引用的自由變量將和這個函數一同存在,即使已經離開了創造它的環境也不例外。所以,有另一種說法認為閉包是由函數和與其相關的引用環境組合而成的實體。

在程序設計語言中,變量可以分為自由變量(free variable)與約束變量(bound variable)兩種。簡單來說,一個函數裏局部變量和參數都被認為是約束變量;而不是約束變量的則是自由變量。

百度百科的解釋可能更加通俗易懂:

閉包是可以包含自由(未綁定到特定對象)變量的代碼塊;這些變量不是在這個代碼塊內或者任何全局上下文中定義的,而是在定義代碼塊的環境中定義(局部變量)。“閉包” 一詞來源於以下兩者的結合:要執行的代碼塊(由於自由變量被包含在代碼塊中,這些自由變量以及它們引用的對象沒有被釋放)和為自由變量提供綁定的計算環境(作用域)。在 Scala、Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby、 Python和Lua,objective c 等語言中都能找到對閉包不同程度的支持。
在編程領域我們可以通俗的說:子函數可以使用父函數中的局部變量,這種行為就叫做閉包!

還是上code吧:還是從Hello, World!開始

auto func = [] { cout << "Hello, World!" << endl; } ();

func();// 調用函數輸出Hello, World!
我們可以像創建變量一樣方便的創建函數!使用STL我們會為了某個很簡單的實現不得不實現一個一個的小函數,a lambda的引入可以讓我們寫出更緊湊的代碼:
bool compare(int i,int j) { return (i>j); }
vector<int> v;
// using default comparison (operator <):
std::sort (v.begin(), v.end());  // using function as comp
std::sort (v.begin(), v.end(), compare); 

// using lambda
std::sort (v.begin(), v.end(), [](int i, int j){ return i > j; });

// Print the contents of the vector.
std::for_each(v.begin(), v.end(), [](int i) { cout << i << " "; });

// C++11 style: using begin(v) instead of v.begin()

std::for_each(begin(v), end(v), [](int i) { cout << i << " "; });

僅僅通過sort和對於vector的遍曆,我們就可以看到a lambda的巨大威力。當然了,對於C++11,我們可以使用“現代風格”進行數組的遍曆:
for( auto d : myvector ) {
  cout<< d <<" ";
}
另外一個快速示例:找到v裏麵大於x並且小於y的第一個元素。在C++11中,最簡單和幹淨的代碼就是調用一個標準函數。
// C++98/03: 直接編寫一個循環 (使用std::find_if會非常困難)
vector<int>::iterator i = v.begin();
for( ; i != v.end(); ++i ) {
if( *i > x && *i < y ) break;
}
 
// C++11: use std::find_if
auto i = find_if( begin(v), end(v), [=](int i) { return i > x && i < y; } );
數組求和:
for_each(begin(v), end(v), [&](int n){ sum += n;)}
最後一個使得代碼高效優雅的例子:
// 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;

// Assign each element in the vector to the sum of the previous two elements.
generate_n(begin(v) + 2, elementCount - 2, [=]() mutable throw() -> int {      
      // Generate current value.
      int n = x + y;
      // Update previous two values.
      x = y;
      y = n;
      return n;
});
下麵對a lambda表達式語法進行詳細的闡述:

Structural elements of a lambda expression

1. lambda-introducer: 定義引用自由變量的方式。

    [] // 沒有定義任何變量。使用未定義變量會導致錯誤。

    [x, &y] // x 以傳值方式傳入(默認),y 以引用方式傳入。

    [&] // 任何被使用到的外部變量皆隱式地以引用方式加以使用。

    [=] // 任何被使用到的外部變量皆隱式地以傳值方式加以使用。

    [&, x] // x 顯示地以傳值方式加以使用。其餘變量以引用方式加以使用。

    [=, &z] // z 顯示地以引用方式加以使用。其餘變量以傳值方式加以使用。

2. lambda-parameter-declaration-list: 參數列表。但是參數不可以有默認值,不可以使用變長參數,不可以有unamed arguments

3. mutable-specification :使得傳值引入的變量可以修改。這個修改因為是修改的外部變量的拷貝,因此並不會影響它本來的值

4. exception-specification:throw()該函數不能拋出異常。如果拋出異常,編譯器將報warning C4297。 throw(...) 可以拋出異常。throw(type)可以拋出type的異常

5. lambda-return-type-clause:如果僅有0/1個return的話可以省略。返回值可以是lambda表達式。

// compile with: /EHsc
#include <functional>
// The following code declares a lambda expression that returns 
// another lambda expression that adds two numbers. 
// The returned lambda expression captures parameter x by value.
auto g = [](int x) -> function<int (int)> { return [=](int y) { return x + y; }; };

// The following code declares a lambda expression that takes another
// lambda expression as its argument.
// The lambda expression applies the argument z to the function f and adds 1.
auto h = [](const function<int (int)>& f, int z) { return f(z) + 1; };

// Call the lambda expression that is bound to h. 
auto a = h(g(7), 8);

最後一個問題, what's the type of a lambda expression? 使用C++11增強的各類function的wrapper std::function, 下麵是使用a lambda作為代理(Delegate)的例子,僅僅是例子,不要問為什麼要這樣用:)
#include <functional>

class EmailProcessor
{
public:
    void receiveMessage (const std::string& message)
    {
        if ( _handler_func ) 
        {
            _handler_func( message );
        }
        // other processing
    }
    void setHandlerFunc (std::function<void (const std::string&)> handler_func)
    {
        _handler_func = handler_func;
    }

private:
        std::function<void (const std::string&)> _handler_func;
};

//當收到Message時,我們希望有個回調函數能夠處理

class MessageSizeStore
{
    MessageSizeStore () : _max_size( 0 ) {}
    void checkMessage (const std::string& message ) 
    {
        const int size = message.length();
        if ( size > _max_size )
        {
            _max_size = size;
        }
    }
    int getSize ()
    {
        return _max_size;
    }

private:
    int _max_size;
};

//我們希望每次Message到來時,統計出曆史的最大長度(好無聊啊。。。)

EmailProcessor processor;
MessageSizeStore size_store;
processor.setHandlerFunc( 
        [&] (const std::string& message) { size_store.checkMessage( message ); } 
);

引用:

https://www.cprogramming.com/c++11/c++11-lambda-closures.html

https://rednaxelafx.iteye.com/blog/184199

https://www.cplusplus.com/reference/algorithm/sort/

Lambda Expression Syntax

https://msdn.microsoft.com/en-us/library/46h7chx6.aspx


https://msdn.microsoft.com/en-us/library/dd293599.aspx


最後更新:2017-04-03 12:53:49

  上一篇:go Tomcat啟動報錯:java.lang.IllegalArgumentException: Can&#39;t convert argument:null
  下一篇:go android mtp簡介