C++函數對象,Lambda,function,bind相關知識

C++函數對象,Lambda,function,bind相關知識

昨天做題時無意間得知了<functional>這個頭文件,之後自己也稍微地瞭解了一下相關的一些知識。

內容比較多,敘述方面可能不是很詳盡,大家如果看完還有不是很理解的地方,建議進文末的鏈接看看。

<functional>頭文件

關於這個頭文件,在cppreference.com中是這樣定義的:

This header is part of the function objects library and provides the standard hash function.

這個頭文件定義了許多函數對象類型和支持函數對象的功能。

函數對象

關於函數對象這裏就來比較詳細的講一下,也爲後面的內容做一個鋪墊。

先來看看cplusplus.com中關於函數對象(Function object)的定義:

Function objects are objects specifically designed to be used with a syntax similar to that of functions. In C++, this is achieved by defining member function operator() in their class.

簡單的說,函數對象是通過重載operator()這個操作符,利用和函數相類似的句法,實現了函數的功能。來看一個例子:

class myFunctionClass{
public:
    int operator()(int x){
        return x;
    }
}test;

int a = test(6);

這個程序中,如果我們輸出a,就會發現,a的值是6,十分簡單易懂。

函數指針

提到了函數對象,就不得不提一下函數指針了。在我學C語言的時候,譚浩強的那本《C語言程序設計》就講解了一下函數指針。類似於我們普通的變量指針的運用,如:

int add(int a, int b){
    return a+b;
}

typedef int(*addNumFunc)(int a, int b);

addNumFunc add2Num = &add;

int a = add2Num(4, 7);

函數指針可以簡化我們的代碼,但是函數對象與之相比有更大的優勢。維基百科上是這樣解說這些優勢的:

函數對象與函數指針相比,有兩個優點:第一是編譯器可以內聯執行函數對象的調用;第二是函數對象內部可以保持狀態。

第一個不用多說,那麼第二個是什麼意思呢?就是將數據保存在成員變量中,以實現攜帶我們需要的數據

std::lambda

lambda(/’læmdə/) 表達式是用於創建匿名函數的,這是C++的一個新的特性。
我們從例子當中來看lambda的語法和其優勢。

 std::sort(x,
           x + n,
           [](int a, int b) { return std::abs(a) < std::abs(b); });

sort函數的第三行就是我們的lambda表達式。它以[]爲開始,沒有函數名稱,接受兩個int類型的參數,返回值是bool類型,也就是a<b的真假。其中返回值是自動推定的,而且只有在一個return的情況下,才能自動推定。

如果我們需要自己定義返回值類型的話,我們可以用如下的方式

[ capture-list ] ( params ) -> ret { body }

比如:

std::cout << [](float f) -> int { return std::abs(f); } (-2.22);

這裏的返回值就是int類型,而且我們在定義了函數之後直接調用該函數。

另外關於lambda表達式開始的這對[],其實也是有其作用的。

  1. []  表示不捕獲任何外部變量
  2. [=]  表示以傳值的方式捕獲
  3. [&]  表示以引用的方式捕獲

當然也可以[&,a]這樣,表示a用值傳遞,而其餘的則是使用引用的方式了。其餘的一次類推,this指針也是可以用的。

lambda表達式的有優點就是讓代碼清晰易懂,同時也避免了和別人所寫的一些代碼的衝突,防止了誤操作等。

閉包類型

關於lambda,在cplusplus.com有這樣一句話。

Constructs a closure: an unnamed function object capable of capturing variables in scope.

所謂閉包類型(ClosureType),就是lambda表達式執行後由編譯器生成自動生成的函數對象有不同的類型名字,並且只有編譯器知道這個類型名字,可以認爲它是一個未命名類型。

下面來看幾個應用:

[]{ std::cout << "Yuuki_Dach's Blog.(The MADAO)" << std::endl; }();
//這裏直接輸出,而且沒有參數傳入,所以最後是()

std::string lambdaReturn = [](const std::string &str)->std::string{
    return "hello " + str; }("hahaya");
std::cout << lambdaReturn << std::endl;
//這裏則是用lambdaReturn這個變量儲存了其結果

auto addFunc = [](int val){std::cout << val + 2 << std::endl; };
    std::for_each(a.begin(), a.end(), addFunc);
//這裏產生了一個匿名對象,保存在func中

std::function

看了前面的auto這個例子的話,就可以講一講std::function了

類模版std::function是一種通用、多態的函數封裝。std::function的實例可以對任何可以調用的目標實體進行存儲、複製、和調用操作,這些目標實體包括普通函數、Lambda表達式、函數指針、以及其它函數對象等。std::function對象是對C++中現有的可調用實體的一種類型安全的包裹。

比如前面的例子中auto部分也能這樣寫:

std::funtion<void(int)> addFunc = [](int val){std::cout << val + 2 << std::endl; };
    std::for_each(a.begin(), a.end(), addFunc);

std::bind

std::bind可用於綁定函數、成員函數、函數對象、成員變量,如:

int testFunc(int a, char c, float f){
    cout << a << endl;
    cout << c << endl;
    cout << f << endl;

    return a;
}

auto bindFunc = std::bind(testFunc, std::placeholders::_2, std::placeholders::_1, 100.1);
    bindFunc('B', 10);

後面的bindFunc部分相當於執行了testFunc(10, ‘B’, 100.1)。

std::placeholders::_1是佔位符,表示對應參數的位置和函數的第一個參數相匹配,以此類推。

關於std::bind,再提兩點比較重要的部分

  • bind預先綁定的參數需要傳具體的變量或值進去,對於預先綁定的參數,是pass-by-value的;
  • 對於不事先綁定的參數,需要傳std::placeholders進去,從_1開始,依次遞增。placeholder是pass-by-reference的。

基本就是這些內容了,有錯誤的地方還請指正。大家也可以看看參考文章,還是很有幫助的(^_^)

另外,歡迎大家來Yuuki_Dach’s Blog

參考文章

  1. C++11 新特性:Lambda 表達式
  2. 函數對象、lambda、function、bind學習
  3. C++11中的std::function
  4. C++ 新特性學習(四) — Bind和Function
  5. C++11中的std::bind
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章