C/C++:std::function 和 std::bind 以及佔位符 std::placeholders::_n 的使用

頭文件:<functional>

std::function<...>

       std::function的實例可以對任何能夠調用的目標實體進行封裝調用,這些目標實體包括普通函數、lambda表達式、函數指針、仿函數、類的普通成員函數和類的靜態成員函數等。std::function對象是對C++中現有的可調用實體的一種類型安全的封裝(我們知道像函數指針這類可調用實體是類型不安全的),示例代碼如下

#include <functional>
#include <iostream>
using namespace std;

std::function<int(int)> func;

// 普通函數
int testFunc(int a)
{
    return a;
}

// lambda表達式
auto lambda = [](int a)->int{ return a; };

// 仿函數(實現operator()的函數)
class Functor
{
public:
    int operator()(int a)
    {
        return a;
    }
};

// 類的普通成員函數和靜態成員函數
class TestClass
{
public:
    int ClassMember(int a) { return a; }
    static int StaticMember(int a) { return a; }
};

int main()
{
    // 普通函數
    func = testFunc;
    int result = func(10);
    cout << "普通函數:"<< result << endl;

    // lambda表達式
    func = lambda;
    result = func(20);
    cout << "lambda表達式:"<< result << endl;

    // 仿函數
    Functor testFunctor;
    func = testFunctor;
    result = func(30);
    cout << "仿函數:"<< result << endl;

    // 類的普通成員函數
    TestClass testObj;
    func = std::bind(&TestClass::ClassMember, &testObj, std::placeholders::_1);
    result = func(40);
    cout << "類的普通成員函數:"<< result << endl;

    // 類的靜態成員函數
    func = TestClass::StaticMember;
    result = func(50);
    cout << "類的靜態成員函數:"<< result << endl;

    return 0;
}

std::bind

       std::bind是這樣一種機制,它可以預先把指定可調用實體的某些參數綁定到已有的變量,產生一個新的可調用實體。可將std::bind函數看作一個通用的函數適配器,它接受一個可調用對象,生成一個新的可調用對象來“適應”原對象的參數列表。std::bind將可調用對象與其參數一起進行綁定,綁定後的結果可以使用std::function保存。std::bind主要有以下幾個作用:

  • 將可調用對象和其參數綁定成一個仿函數
  • 只綁定部分參數,減少可調用對象傳入的參數
  • 改變參數綁定順序

std::bind綁定普通函數

double myDivide(double x, double y) 
{
    return x/y;
}
auto fn_half = std::bind(myDivide, 1, 2);  
std::cout << fn_half() << std::endl;                        // 0.5

       bind的第一個參數是函數名,普通函數做實參時,會隱式轉換成函數指針。因此std::bind (myDivide,1,2)等價於std::bind (&myDivide,1,2)

std::bind綁定一個成員函數

struct Foo {
    void print_sum(int n1, int n2)
    {
        std::cout << n1+n2 << '\n';
    }
    int data = 10;
};
int main() 
{
    Foo foo;
    auto f = std::bind(&Foo::print_sum, &foo, 95, 5);
    f(); // 100
}

       bind綁定類成員函數時,第一個參數表示對象的成員函數的指針,第二個參數表示對象的地址。必須顯示的指定&Foo::print_sum,因爲編譯器不會將對象的成員函數隱式轉換成函數指針,所以必須在Foo::print_sum前添加&。使用對象成員函數的指針時,必須要知道該指針屬於哪個對象,因此第二個參數爲對象的地址 &foo;

std::bind綁定一個引用參數

默認情況下,bind的那些不是佔位符(佔位符的概念後面介紹)的參數被拷貝到bind返回的可調用對象中。但是,與lambda類似,有時對有些綁定的參數希望以引用的方式傳遞,或是要綁定參數的類型無法拷貝。

#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
#include <sstream>
using namespace std::placeholders;
using namespace std;

ostream & print(ostream &os, const string& s, char c)
{
    os << s << c;
    return os;
}

int main()
{
    vector<string> words{"hello", "world", "this", "is", "C++11"};
    ostringstream os;
    char c = ' ';
    for_each(words.begin(), words.end(), 
                   [&os, c](const string & s){os << s << c;} );
    cout << os.str() << endl;

    ostringstream os1;
    // ostream不能拷貝,若希望傳遞給bind一個對象,
    // 而不拷貝它,就必須使用標準庫提供的ref函數
    for_each(words.begin(), words.end(),
                   bind(print, ref(os1), _1, c));
    cout << os1.str() << endl;
}

std::placeholders::_n

前面的 std::bind 可以綁定一個可調用對象和調用這個可調用對象所需要的參數,比如上面的例子

double myDivide(double x, double y) 
{
    return x/y;
}
auto fn_half = std::bind(myDivide, 1, 2);  
std::cout << fn_half() << std::endl;                        // 0.5

       這段代碼中我們把可調用的函數封裝在了fn_half中,並把想傳入的參數同時傳了進去,因此結果是0.5,但如果我們只確定了一個想傳入的參數,另一個想在調用std::bind返回的fn_half中使用,那麼我們就需要佔位符,因此可以概括一下佔位符的主要作用可以減少調用參數的數量,也可以將調用參數的位置互換。代碼如下

// 減少1個參數
double myDivide(double x, double y) 
{
    return x/y;
}
auto fn_half = std::bind(myDivide, std::placeholders::_1, 2);  
std::cout << fn_half(1) << std::endl;                           // 0.5

// 減少2個參數
double myDivide(double x, double y) 
{
    return x/y;
}
auto fn_half = std::bind(myDivide, 1, 2);  
std::cout << fn_half() << std::endl;                            // 0.5

// 參數位置互換
double myDivide(double x, double y) 
{
    return x/y;
}
auto fn_half = std::bind(myDivide, std::placeholders::_2, std::placeholders::_1);  
std::cout << fn_half(1, 2) << std::endl;                        // 2.0

注意:如果在bind函數中使用了特定數量佔位符,那麼在調用bind函數返回的可調用對象時,也要有同等數量的參數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章