C++提供了多個包裝器,它們主要是為了給其他編程接口提供更一致或更合適的接口。C++11提供了多個包裝器,這里我們重點了解一下包裝器function
。
對于function
, C++ 參考手冊給出的定義為:
類模板 std::function
是通用多態函數封裝器。std::function
的實例能存儲、復制及調用任何可調用 (Callable) 目標——函數、 lambda 表達式、 bind 表達式或其他函數對象,還有指向成員函數指針和指向數據成員指針。
C++11為什么要引入function
我們先看一個例子:
#include < iostream >
template < typename T, typename F >
T use_f(T v, F f) {
static int count = 0;
count++;
std::cout < < " use_f count = " < < count < < ", &count = " < < &count
< < std::endl;
return f(v);
}
class Fp {
private:
double z_;
public:
Fp(double z = 1.0) : z_(z) {}
double operator()(double p) { return z_ * p; }
};
class Fq {
private:
double z_;
public:
Fq(double z = 1.0) : z_(z) {}
double operator()(double q) { return z_ + q; }
};
double dub(double x) { return 2.0 * x; }
double square(double x) { return x * x; }
int main() {
using std::cout;
using std::endl;
double y = 1.21;
cout < < "Function pointer dub:n";
/*dub是函數名(函數名是一個指針),
因此參數F對應的類型為double(*)double,
即一個指向(接受一個double參數并返回一個double的)函數指針*/
cout < < " " < < use_f(y, dub) < < endl;
cout < < "Function pointer square:n";
cout < < " " < < use_f(y, square) < < endl;//同上
cout < < "Function object Fp:n";
//Fp(5.0)是一個參數對象,F對應的類型為Fp
cout < < " " < < use_f(y, Fp(5.0)) < < endl;
cout < < "Function object Fq:n";
//Fq(5.0)是一個參數對象,F對應的類型為Fq
cout < < " " < < use_f(y, Fq(5.0)) < < endl;
cout < < "Lambda expression 1:n";
//此處是一個lambda表達式,F對應的類型為該lambda表達式使用的類型
cout < < " " < < use_f(y, [](double u) { return u * u; }) < < endl;
cout < < "Lambda expression 2:n";
////此處是一個lambda表達式,F對應的類型為該lambda表達式使用的類型
cout < < " " < < use_f(y, [](double u) { return u + u / 2.0; }) < < endl;
return 0;
}
程序通過一個模板函數use_f
使用參數f
表示調用的類型,然后將f(v)
返回。在主函數中我們6次調用模板函數,對于前兩個調用的use_f
為同一個實例化。后面四個,每一個都有其對應use_f
的實例化。實際上,上述代碼的運行結果如下:
Function pointer dub:
use_f count = 1, &count = 0x555555756140
2.42
Function pointer square:
use_f count = 2, &count = 0x555555756140
1.4641
Function object Fp:
use_f count = 1, &count = 0x555555756144
6.05
Function object Fq:
use_f count = 1, &count = 0x555555756148
6.21
Lambda expression 1:
use_f count = 1, &count = 0x555555756138
從運行結果中,我們可以看出,在這六次調用中use_f
的實例化了5次。使用模板函數,看似統一了操作形式,但其對于不同類型的F
對模板函數都要進行一次實例化,這大大增加了編譯的時長,并使頭文件也增大,同時也降低了代碼的執行效率。為了解決這類問題,我們首先能想到的解決辦法就是:降低use_f
的實例化的次數,理想的情況下是:在這6次循環調用的時候,調用同一個use_f
的實例。針對上述例子,根據代碼注釋的分析,如果我們能將這6次調用中模板函數中F
的類型保持統一,就可以像第一、二次調用的情況類似,使這六次調用同一個use_f
的實例成為可能。
針對例子中的函數指針、函數對象和lambda表達式,它們有一個共同的特征:都是接受一個double
參數并返回一個double
值。也就是它們的調用特征標(它們的特征標都是double(double)
)相同。這便是function
解決這個問題的關鍵。【 注 :調用特征標是由返回類型和參數類型列表決定的,其格式為:返回類型(參數類型列表)
,其中每個參數類型用逗號分隔。】
因此,C++11引入了function
包裝器。function
包裝器可以簡單理解為一個接口,它可以將特征標相同的函數指針、函數對象和lambda表達式等統一定義為一類特殊的對象。
function的用法
包裝器function
的本質是一個模板,它是在頭文件functional
中聲明,其使用方法如下:
template< class >
class function;
template< class R, class... Args >
class function< R(Args...) >;
其中R
為被調用函數的返回類型,Args
為被調用函數的形參。存儲的可調用對象被稱為std::function
的目標,如果目標為空,則調用空的function
會導致拋出std::bad_function_call
異常。
示例:
#include < functional >
#include < iostream >
void print_num(int i) { std::cout < < i < < 'n'; }
int main() {
// 存儲自由函數
std::function< void(int) > f_display = print_num;
f_display(-9);
// 存儲 lambda
std::function< double(double) > f_dub = [](double a) { return 2.0 * a; };
std::cout < < f_dub(2) < < 'n';
// 存儲目標為空
std::function< int() > f = nullptr;
try {
f();
} catch (const std::bad_function_call& e) { //拋出異常
std::cout < < e.what() < < 'n';
}
}
輸出結果為:
-9
4
bad_function_call
在了解完function
的用法之后,回到我們最開始的問題,其中,6次循環中要處理的目標的特征標均為double(double)
,因此,我們班使用function
包裝器將它們將統一“包裝”成function
use_f
將只實例化一次。使用function
包裝器改進后的代碼如下所示:
#include < functional >
#include < iostream >
template < typename T, typename F >
T use_f(T v, F f) {
static int count = 0;
count++;
std::cout < < " use_f count = " < < count < < ", &count = " < < &count
< < std::endl;
return f(v);
}
class Fp {
private:
double z_;
public:
Fp(double z = 1.0) : z_(z) {}
double operator()(double p) { return z_ * p; }
};
class Fq {
private:
double z_;
public:
Fq(double z = 1.0) : z_(z) {}
double operator()(double q) { return z_ + q; }
};
double dub(double x) { return 2.0 * x; }
double square(double x) { return x * x; }
int main() {
using std::cout;
using std::endl;
using std::function;
double y = 1.21;
typedef function< double(double) > fdd; // simplify the type declaration
cout < < "Function pointer dub:n";
cout < < " " < < use_f(y, fdd(dub)) < < endl;
cout < < "Function pointer square:n";
cout < < " " < < use_f(y, fdd(square)) < < endl;
cout < < "Function object Fp:n";
cout < < " " < < use_f(y, fdd(Fq(10.0))) < < endl;
cout < < "Function object Fq:n";
cout < < " " < < use_f(y, fdd(Fp(10.0))) < < endl;
cout < < "Lambda expression 1:n";
cout < < " " < < use_f(y, fdd([](double u) { return u * u; })) < < endl;
cout < < "Lambda expression 2:n";
cout < < " " < < use_f< double >(y, fdd([](double u) { return u + u / 2.0; }))
< < endl;
return 0;
}
輸出結果為:
Function pointer dub:
use_f count = 1, &count = 0x555555758134
2.42
Function pointer square:
use_f count = 2, &count = 0x555555758134
1.4641
Function object Fp:
use_f count = 3, &count = 0x555555758134
11.21
Function object Fq:
use_f count = 4, &count = 0x555555758134
12.1
Lambda expression 1:
use_f count = 5, &count = 0x555555758134
1.4641
Lambda expression 2:
use_f count = 6, &count = 0x555555758134
1.815
從輸出結果可以看出,
use_f
確實只實例化了一次,增加了編碼效率,6次循環調用同一個函數,增加了代碼額執行效率。
總結
function
包裝器將可調用對象的類型進行統一,便于我們對其進行統一化管理,同時,使用function
包裝器可以解決模板效率低下,實例化多份的問題。
-
存儲器
+關注
關注
38文章
7484瀏覽量
163763 -
C++語言
+關注
關注
0文章
147瀏覽量
6989 -
封裝器
+關注
關注
0文章
7瀏覽量
5866
發布評論請先 登錄
相關推薦
評論