C++ 重载函数调用运算符 | 再探lambda,函数对象,可调用对象
文章目錄
- 重載函數調用運算符
- lambda
- lambda等價于函數對象
- lambda等價于類
- 標準庫函數對象
- 可調用對象與function
- 可調用對象
- function
- 函數重載與function
重載函數調用運算符
函數調用運算符必須是成員函數。 一個類可以定義多個不同版本的調用運算符,互相之間應該在參數數量or類型上有所區別。如果類定義了函數調用運算符 () ,則該類的對象稱作函數對象,因為可以調用這種對象,所以我們說這些對象的行為像函數一樣。
class absInt { public:int operator()(int val)const {return val < 0 ? -val : val;} };lambda
lambda等價于函數對象
對于作為最后一個實參的 lambda 的表達式來說:
//根據單詞的長度對其進行排序,對于長度相同的單詞按照字母表順序排序 stable_sort (words.begin(), words.end(),[](const string &a, const string &b){return a.size () < b.size();});其等價于下面這個類的一個未命名對象:
class ShorterString { public:bool operator()(const string &s1, const string &s2) const{ return s1.size() < s2.size(); } };默認情況下,lambda 不能改變它捕獲的變量,因此類的函數調用運算符是一個 const 成員函數。
那么我們可以重寫并調用 stable_sort :
stable_sort(words.begin(), words.end(), ShorterString());lambda等價于類
- 當一個 lambda表達式 通過引用捕獲變量時,將由程序負責確保 lambda 執行時所引用的對象確實存在。因此,編譯器可以直接使用該引用而無須在 lambda產生的類 中將其存儲為數據成員。
- 過值捕獲的變量被拷貝到 lambda 中。因此,這種 lambda 產生的類必須為每個值捕獲的變量建立對應的數據成員,同時創建構造函數,令其使用捕獲的變量的值來初始化數據成員。
舉個例子,有這樣一個 lambda,它的作用是找到第一個長度不小于給定值的 string 對象:
//獲得第一個指向滿足條件元素的迭代器,該元素滿足 size() >= sz auto wc = find_if(words.begin(), words.end(),[sz] (const string &a){ return a.size() >= sz;});等價于這樣的類:
class SizeComp{size_t sz; // 該數據成員對應通過值捕獲的變量 public:SizeComp (size_t n): sz(n) {} // 該形參對應捕獲的變量// 該調用運算符的返回類型、形參和函數體都與lambda一致bool operator()(const string &s) const { return s.size()>= sz; }由于這個類不含默認構造函數,因此想要調用它必須提供一個實參,也就起到了 值捕獲 的作用。
標準庫函數對象
表示運算符的函數對象類常用來替換算法中的默認運算符。
例如,在默認情況下排序算法使用 operator< 將序列按照升序排列。如果要執行降序排列的話,我們可以傳入一個 greater 類型的對象。該類將產生一個調用運算符并負責執行待排序類型的大于運算:
// sv是一個vecotr<string>,greater<string>()是一個臨時的函數對象 sort(sv.begin(), sv.end(), greater<string>());標準庫函數對象適用性是很寬的,甚至適用于指針:
而實際上,STL 中也正是這樣做的:關聯容器使用 less<key_type> 對元素排序,因此我們可以定義一個指針的 set 或者在 map 中使用指針作為關鍵值而無須直接聲明 less。
可調用對象與function
可調用對象
C++語言中有幾種可調用的對象:函數、函數指針、lambda表達式、bind創建的對象、重載了函數調用運算符的類。
和其他對象一樣,可調用的對象也有類型。例如,每個lambda有它自己唯一的(未命名)類類型;函數及函數指針的類型則由其返回值類型和實參類型決定,等等。
然而,兩個不同類型的可調用對象卻可能共享同一種調用形式(call signature)。 調用形式指明了調用返回的類型以及傳遞給調用的實參類型。一種調用形式對應一個函數類型,例如:
int(int, int)是一個函數類型,它接受兩個 int、返回一個 int。
用具體的例子來講,即:
上面三個可調用對象共享一種調用形式—— int(int, int)
對于調用形式相同的可調用對象,我們可以定義一個函數表(function table)存儲他們的“指針”,對他們進行統一管理。 當程序需要執行特定的 +、%、/ 時,從表中查找該操作對應的可調用對象。
在 C++ 中,函數表很容易通過 map 實現:
// string表示 + 或 % 或 / // 函數指針 作為 value map<string, int(*)(int, int)> m; // 我們可以將 add的指針 加入到 m 中 m.insert({"+", add}); // {"+", add}是一個pair // 但不可以將 mod 或者 divide 存入 m m.insert({"%", mod}); // error:mod不是一個函數指針,lambda有自己的類類型function
function 模板的運用示例:
function 也可以和 map 搭配使用:
初始化:
使用:
函數重載與function
我們不能直接將重載函數的名字存入 function 類型的對象中:
int add(int i, int j) { return i + j; } A add(const A&, const A&); map<string, function<int(int, int)>> m; m.insert( {"+", add} ); // ERROR:哪個add?解決上述二義性問題的一條途徑是存儲函數指針而非函數名字:
int (*fp)(int, int) = add; m.insert( {"+", fp} );新版本標準庫中的 function 類與舊版本中的 unary_function 和 binary_function 沒有關聯。后兩個類已經被更通用的 bind 函數替代了。
總結
以上是生活随笔為你收集整理的C++ 重载函数调用运算符 | 再探lambda,函数对象,可调用对象的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 蜂云分期订单什么意思
- 下一篇: 农村信用社存款利率表2022最新利率,分