小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

C++ function、bind以及l(fā)amda表達(dá)式

 waston 2018-08-09

 本文是C++0x系列的第四篇,主要是內(nèi)容是C++0x中新增的lambda表達(dá)式, function對象和bind機(jī)制。之所以把這三塊放在一起講,是因?yàn)檫@三塊之間有著非常密切的關(guān)系,通過對比學(xué)習(xí),加深對這部分內(nèi)容的理解。在開始之間,首先要講一個概念,closure(閉包),這個概念是理解lambda的基礎(chǔ)。下面我們來看看wikipedia上對于計算機(jī)領(lǐng)域的closure的定義:

A closure (also lexical closure, function closure or function value) is a function together with a referencing environment for the non-local variables of that function.

上面的大義是說,closure是一個函數(shù)和它所引用的非本地變量的上下文環(huán)境的集合。從定義我們可以得知,closure可以訪問在它定義范圍之外的變量,也即上面提到的non-local vriables,這就大大增加了它的功力。關(guān)于closure的最重要的應(yīng)用就是回調(diào)函數(shù),這也是為什么這里把function, bind和lambda放在一起講的主要原因,它們?nèi)咴谑褂没卣{(diào)函數(shù)的過程中各顯神通。下面就為大家一步步接開這三者的神秘面紗。

1. function

我們知道,在C++中,可調(diào)用實(shí)體主要包括函數(shù),函數(shù)指針,函數(shù)引用,可以隱式轉(zhuǎn)換為函數(shù)指定的對象,或者實(shí)現(xiàn)了opetator()的對象(即C++98中的functor)。C++0x中,新增加了一個std::function對象,std::function對象是對C++中現(xiàn)有的可調(diào)用實(shí)體的一種類型安全的包裹(我們知道像函數(shù)指針這類可調(diào)用實(shí)體,是類型不安全的)。我們來看幾個關(guān)于function對象的例子:

  • #include < functional>
  • std::function< size_t (const char*) > print_func;
  • /// normal function -> std::function object
  • size_t CPrint(const char*) { ... }
  • print_func = CPrint;
  • print_func("hello world"):
  • /// functor -> std::function object
  • class CxxPrint
  • {
  • public:
  • size_t operator()(const char*) { ... }
  • };
  • CxxPrint p;
  • print_func = p;
  • print_func("hello world");
  • 在上面的例子中,我們把一個普通的函數(shù)和一個functor賦值給了一個std::function對象,然后我們通過該對象來調(diào)用。其它的C++中的可調(diào)用實(shí)體都可以像上面一樣來使用。通過std::function的包裹,我們可以像傳遞普通的對象一樣來傳遞可調(diào)用實(shí)體,這樣就很好解決了類型安全的問題。了解了std::function的基本用法,下面我們來看一些使用過程中的注意事項(xiàng):

    • (1)關(guān)于可調(diào)用實(shí)體轉(zhuǎn)換為std::function對象需要遵守以下兩條原則: a. 轉(zhuǎn)換后的std::function對象的參數(shù)能轉(zhuǎn)換為可調(diào)用實(shí)體的參數(shù)
      b. 可高用實(shí)體的返回值能轉(zhuǎn)換為std::function對象的(這里注意,所有的可調(diào)用實(shí)體的返回值都與返回void的std::function對象的返回值兼容)。
    • (2)std::function對象可以refer to滿足(1)中條件的任意可調(diào)用實(shí)體
    • (3)std::function object最大的用處就是在實(shí)現(xiàn)函數(shù)回調(diào),使用者需要注意,它不能被用來檢查相等或者不相等
    2. bind

    bind是這樣一種機(jī)制,它可以預(yù)先把指定可調(diào)用實(shí)體的某些參數(shù)綁定到已有的變量,產(chǎn)生一個新的可調(diào)用實(shí)體,這種機(jī)制在回調(diào)函數(shù)的使用過程中也頗為有用。C++98中,有兩個函數(shù)bind1st和bind2nd,它們分別可以用來綁定functor的第一個和第二個參數(shù),它們都是只可以綁定一個參數(shù)。各種限制,使得bind1st和bind2nd的可用性大大降低。C++0x中,提供了std::bind,它綁定的參數(shù)的個數(shù)不受限制,綁定的具體哪些參數(shù)也不受限制,由用戶指定,這個bind才是真正意義上的綁定,有了它,bind1st和bind2nd就沒啥用武之地了,因此C++0x中不推薦使用bind1st和bind2nd了,都是deprecated了。下面我們通過例子,來看看bind的用法:

  • #include < functional>
  • int Func(int x, int y);
  • auto bf1 = std::bind(Func, 10, std::placeholders::_1);
  • bf1(20); ///< same as Func(10, 20)
  • class A
  • {
  • public:
  • int Func(int x, int y);
  • };
  • A a;
  • auto bf2 = std::bind(&A::Func, a, std::placeholders::_1, std::placeholders::_2);
  • bf2(10, 20); ///< same as a.Func(10, 20)
  • std::function< int(int)> bf3 = std::bind(&A::Func, a, std::placeholders::_1, 100);
  • bf3(10); ///< same as a.Func(10, 100)
  • 上面的例子中,bf1是把一個兩個參數(shù)普通函數(shù)的第一個參數(shù)綁定為10,生成了一個新的一個參數(shù)的可調(diào)用實(shí)體體; bf2是把一個類成員函數(shù)綁定了類對象,生成了一個像普通函數(shù)一樣的新的可調(diào)用實(shí)體; bf3是把類成員函數(shù)綁定了類對象和第二個參數(shù),生成了一個新的std::function對象??炊松厦娴睦?,下面我們來說說使用bind需要注意的一些事項(xiàng):

    • (1)bind預(yù)先綁定的參數(shù)需要傳具體的變量或值進(jìn)去,對于預(yù)先綁定的參數(shù),是pass-by-value的
    • (2)對于不事先綁定的參數(shù),需要傳std::placeholders進(jìn)去,從_1開始,依次遞增。placeholder是pass-by-reference的
    • (3)bind的返回值是可調(diào)用實(shí)體,可以直接賦給std::function對象
    • (4)對于綁定的指針、引用類型的參數(shù),使用者需要保證在可調(diào)用實(shí)體調(diào)用之前,這些參數(shù)是可用的
    • (5)類的this可以通過對象或者指針來綁定
    3. lambda

    講完了function和bind, 下面我們來看lambda。有python基礎(chǔ)的朋友,相信對于lambda不會陌生??吹竭@里的朋友,請再回憶一下前面講的closure的概念,lambda就是用來實(shí)現(xiàn)closure的東東。它的最大用途也是在回調(diào)函數(shù),它和前面講的function和bind有著千絲萬縷的關(guān)系。下面我們先通過例子來看看lambda的廬山真面目:

  • vector< int> vec;
  • /// 1. simple lambda
  • auto it = std::find_if(vec.begin(), vec.end(), [](int i) { return i > 50; });
  • class A
  • {
  • public:
  • bool operator(int i) const { return i > 50; }
  • };
  • auto it = std::find_if(vec.begin(), vec.end(), A());
  • /// 2. lambda return syntax
  • std::function< int(int)> square = [](int i) -> int { return i * i; }
  • /// 3. lambda expr: capture of local variable
  • {
  • int min_val = 10;
  • int max_val = 1000;
  • auto it = std::find_if(vec.begin(), vec.end(), [=](int i) {
  • return i > min_val && i < max_val;
  • });
  • auto it = std::find_if(vec.begin(), vec.end(), [&](int i) {
  • return i > min_val && i < max_val;
  • });
  • auto it = std::find_if(vec.begin(), vec.end(), [=, &max_value](int i) {
  • return i > min_val && i < max_val;
  • });
  • }
  • /// 4. lambda expr: capture of class member
  • class A
  • {
  • public:
  • void DoSomething();
  • private:
  • std::vector<int> m_vec;
  • int m_min_val;
  • int m_max_va;
  • };
  • /// 4.1 capture member by this
  • void A::DoSomething()
  • {
  • auto it = std::find_if(m_vec.begin(), m_vec.end(), [this](int i){
  • return i > m_min_val && i < m_max_val; });
  • }
  • /// 4.2 capture member by default pass-by-value
  • void A::DoSomething()
  • {
  • auto it = std::find_if(m_vec.begin(), m_vec.end(), [=](int i){
  • return i > m_min_val && i < m_max_val; });
  • }
  • /// 4.3 capture member by default pass-by-reference
  • void A::DoSomething()
  • {
  • auto it = std::find_if(m_vec.begin(), m_vec.end(), [&](int i){
  • return i > m_min_val && i < m_max_val; });
  • }
  • 上面的例子基本覆蓋到了lambda表達(dá)的基本用法。我們一個個來分析每個例子(標(biāo)號與上面代碼注釋中1,2,3,4一致):

    • (1)這是最簡單的lambda表達(dá)式,可以認(rèn)為用了lambda表達(dá)式的find_if和下面使用了functor的find_if是等價的
    • (2)這個是有返回值的lambda表達(dá)式,返回值的語法如上面所示,通過->寫在參數(shù)列表的括號后面。返回值在下面的情況下是可以省略的:
      a. 返回值是void的時候
      b. lambda表達(dá)式的body中有return expr,且expr的類型與返回值的一樣
    • (3)這個是lambda表達(dá)式capture本地局部變量的例子,這里三個小例子,分別是capture時不同的語法,第一個小例子中=表示capture的變量pass-by-value, 第二個小拿出中&表示capture的變量pass-by-reference,第三個小例子是說指定了default的pass-by-value, 但是max_value這個單獨(dú)pass-by-reference
    • (4)這個是lambda表達(dá)式capture類成員變量的例子,這里也有三個小例子。第一個小例子是通過this指針來capture成員變量,第二、三個是通過缺省的方式,只不過第二個是通過pass-by-value的方式,第三個是通過pass-by-reference的

    分析完了上面的例子,我們來總結(jié)一下關(guān)于lambda表達(dá)式使用時的一些注意事項(xiàng):

    • (1)lambda表達(dá)式要使用引用變量,需要遵守下面的原則:
      a. 在調(diào)用上下文中的局部變量,只有capture了才可以引用(如上面的例子3所示)
      b. 非本地局部變量可以直接引用
    • (2)使用者需要注意,closure(lambda表達(dá)式生成的可調(diào)用實(shí)體)引用的變量(主要是指針和引用),在closure調(diào)用完成之前,必須保證可用,這一點(diǎn)和上面bind綁定參數(shù)之后生成的可調(diào)用實(shí)體是一致的
    • (3)關(guān)于lambda的用處,就是用來生成closure,而closure也是一種可調(diào)用實(shí)體,所以可以通過std::function對象來保存生成的closure,也可以直接用auto

    通過上面的介紹,我們基本了解了function, bind和lambda的用法,把三者結(jié)合起來,C++將會變得非常強(qiáng)大,有點(diǎn)函數(shù)式編程的味道了。最后,這里再補(bǔ)充一點(diǎn),對于用bind來生成function和用lambda表達(dá)式來生成function, 通常情況下兩種都是ok的,但是在參數(shù)多的時候,bind要傳入很多的std::placeholders,而且看著沒有l(wèi)ambda表達(dá)式直觀,所以通常建議優(yōu)先考慮使用lambda表達(dá)式。

      本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
      轉(zhuǎn)藏 分享 獻(xiàn)花(0

      0條評論

      發(fā)表

      請遵守用戶 評論公約

      類似文章 更多