頭文件:<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函數返回的可調用對象時,也要有同等數量的參數。