可調(diào)用對(duì)象

A successful book is not made of what is in it, but what is left out of it.
?????????????????????????????? ???— Mark Twain

接觸C++ 2.0已經(jīng)有段時(shí)間了齐佳,簡(jiǎn)單總結(jié)一下C++中認(rèn)為是函數(shù)的東西篷扩,或者說(shuō)類(lèi)似于函數(shù)的東西,我們從標(biāo)準(zhǔn)的C和C++函數(shù)到函數(shù)對(duì)象和lambda表達(dá)式慢慢講起嗡髓。

1. 常規(guī)函數(shù)

C++中定義函數(shù)的方式有很多本涕,下面我們舉例說(shuō)明业汰,對(duì)于一些耳熟能詳?shù)母拍罹鸵粠Ф^(guò)。

  • 標(biāo)準(zhǔn)C函數(shù)
bool greater(int arg1, int arg2) { return arg1 > arg2; }
  • 類(lèi)成員函數(shù)
struct number {
  bool greater(int arg1, int arg2) { return arg1 > arg2; }
};

說(shuō)明一下菩颖,在C++中我一般只使用struct來(lái)定義類(lèi)样漆,而不是使用傳統(tǒng)的class,后面我所有的文字都會(huì)如此晦闰,這純粹是個(gè)人的編程風(fēng)格放祟,讀者只要保持自己的習(xí)慣,并一如既往堅(jiān)持下去就行呻右。

  • 類(lèi)靜態(tài)函數(shù)
struct number {
  static bool greater(int arg1, int arg2) { return arg1 > arg2; }
};

前面關(guān)于普通的函數(shù)舞竿,類(lèi)的成員函數(shù)以及類(lèi)的靜態(tài)方法,相信讀者已經(jīng)耳熟能詳了窿冯,下面介紹一下C++ 2.0的新式函數(shù)定義。

  • C++ 2.0的新式函數(shù)
    c++11提供了一種新式函數(shù)的寫(xiě)法确徙,在函數(shù)的末尾說(shuō)明函數(shù)的返回類(lèi)型醒串,函數(shù)開(kāi)頭使用auto關(guān)鍵字执桌,這一用法主要用于編寫(xiě)函數(shù)模板。
// C++ 11
auto greater(int arg1, int arg2) -> bool {    /* 尾置返回類(lèi)型 */
    return arg1 > arg2;
}

上面的用法并不常用芜赌,在c++14以后可以完全忽略返回值類(lèi)型仰挣,而由編譯器根據(jù)return語(yǔ)句自動(dòng)推斷,這里的牽涉內(nèi)容比較多缠沈,就不詳細(xì)展開(kāi)了膘壶,簡(jiǎn)單舉兩個(gè)例子。

// C++ 14
int value = 3;
auto answer()  { return value ; }     /* 返回類(lèi)型 int */
const auto& answer()  { return value ; }     /* 返回類(lèi)型 const int& */

// 還可以使用 decltype 關(guān)鍵字
decltype(auto) answer()  { return value ; }

2. 函數(shù)指針

函數(shù)指針(function pointer)它是一個(gè)存放函數(shù)地址的變量洲愤,可以通過(guò)這個(gè)變量調(diào)用該函數(shù)颓芭。在c++11之前一般使用typedef關(guān)鍵字去定義函數(shù)指針類(lèi)型,在c++11之后可以使用更具表現(xiàn)力的using來(lái)替代柬赐。

bool greater(int arg1, int arg2) { return arg1 > arg2; }

// C++11以前
typedef bool (*old_cmp)(int, int);
old_cmp cmp = greater;

// C++11以后
using new_cmp = bool (*)(int, int);
new_cmp  cmp = greater;

3. 仿函數(shù)

其實(shí)亡问,在C++中一直可以定義和使用像函數(shù)一樣的對(duì)象,它們被稱(chēng)為仿函數(shù)(Functor)肛宋。本質(zhì)上州藕,就是一個(gè)重載的調(diào)用操作符的類(lèi)(call operator),即定義了operator()的類(lèi)酝陈,可以有任意個(gè)數(shù)和任意類(lèi)型的參數(shù)床玻。

struct functor { 
    return_type operator()(args...) const { ... }
};

另外,值得提一下的是沉帮,根據(jù)operator()包含的0個(gè)锈死、1個(gè)或2個(gè)參數(shù),這種Functor分別被稱(chēng)為生成器遇西、一元仿函數(shù)或二元仿函數(shù)馅精,下面分別舉例說(shuō)明。

  • 生成器
struct increase_generator { 
  int operator()() noexcept { return num++; }
private:
  int num = 0;
};

工作原理非常簡(jiǎn)單粱檀,每次調(diào)用increase_generator::operator()時(shí)洲敢,將成員變量num的值返回,并將num的值增加1茄蚯。

int main() {
  increase_generator num_generator;
  for (int i = 0; i < 3; ++i) {
    std::cout << num_generator() << std::endl;
  }
}
// output: 0 1 2
  • 一元仿函數(shù)
struct cube { 
  constexpr int operator()(const int value) const noexcept { return value * value * value; }
};

顧名思義压彭,這個(gè)仿函數(shù)對(duì)它傳遞的值做了立方運(yùn)算,并且這個(gè)operator()被聲明為const渗常,它的行為類(lèi)似于數(shù)學(xué)上的純函數(shù)壮不,即無(wú)副作用。這里constexpr的作用皱碘,有興趣的讀者可以自行去研究一下询一。

  • 謂詞
    一元仿函數(shù)一個(gè)常用的用途就是當(dāng)做謂詞(predict),即只有一個(gè)參數(shù)且返回值為bool類(lèi)型的仿函數(shù)。如下:
struct is_even { 
  constexpr bool operator()(const int value) const noexcept { return (value % 2) == 0; }
};

舉個(gè)使用的例子

int main() {
  std::vector<int> numbers{1, 2, 3, 4, 5};
  numbers.erase(std::remove_if(std::begin(numbers), std::end(numbers), is_even()), 
                std::end(numbers));

  std::copy(std::begin(numbers), std::end(numbers), std::ostream_iterator<int>{std::cout, " "});
  return 0;
}
// output: 1 3 5 

上面的示例使用了Erase-remove慣用法健蕊,結(jié)合我們定義的is_even仿函數(shù)菱阵,實(shí)現(xiàn)了對(duì)vector中偶數(shù)元素的刪除。

  • 二元仿函數(shù)
struct greater { 
  bool operator()(const auto& v1, const auto& v2) const noexcept { return v1 > v2; }
};

4. lambda

  • 看個(gè)例子
    我們把上面實(shí)現(xiàn)的仿函數(shù)is_even缩功,同樣的用lambda去實(shí)現(xiàn)晴及,如下
auto is_even = [] (auto item) { return (item % 2) == 0; };

is_even(2);  // 返回true

可以看到,使用lambda的實(shí)現(xiàn)更加簡(jiǎn)短嫡锌,表現(xiàn)力也更豐富虑稼,不過(guò)通常lambda表達(dá)式使用都會(huì)內(nèi)聯(lián)實(shí)現(xiàn),即在應(yīng)用時(shí)實(shí)現(xiàn)势木。
注:上面的語(yǔ)法需要支持C++14及以上的編譯器才可以編譯成功蛛倦。

  • 語(yǔ)法
[capture list] (param list) -> return_type { lambda body;}

說(shuō)明

  • [capture list] 捕獲列表,用于捕獲外層變量
    [] 不捕獲任何變量
    [&] 捕獲外部作用域中所有變量跟压,并作為引用在匿名函數(shù)體中使用
    [=] 捕獲外部作用域中所有變量胰蝠,并拷貝一份在匿名函數(shù)體中使用
    [x, &y] x按值捕獲, y按引用捕獲
    [&, x] x按值捕獲. 其它變量按引用捕獲
    [=, &y] y按引用捕獲. 其它變量按值捕獲
    [this] 捕獲當(dāng)前類(lèi)中的this指針,如果已經(jīng)使用了&或者=就默認(rèn)添加此選項(xiàng)
  • (param list) 參數(shù)列表
    當(dāng)匿名函數(shù)沒(méi)有參數(shù)時(shí)震蒋,可以省略(param list)部分
  • -> return_type 返回值類(lèi)型
    C++14以后可以省略
  • { lambda body;} 函數(shù)實(shí)現(xiàn)

5. std::function包裝函數(shù)對(duì)象

std::function 是一個(gè)可調(diào)用對(duì)象包裝器茸塞,是一個(gè)類(lèi)模板,可以容納上述所有的可調(diào)用對(duì)象查剖,它可以用統(tǒng)一的方式處理函數(shù)钾虐、函數(shù)對(duì)象、函數(shù)指針笋庄,并允許保存和延遲它們的執(zhí)行效扫。

  • 作用
    對(duì)函數(shù)指針(包括普通函數(shù),類(lèi)成員函數(shù)直砂,類(lèi)靜態(tài)成員函數(shù))菌仁,仿函數(shù),lambda表達(dá)式做類(lèi)型消除静暂,也就是說(shuō)可以將這些可調(diào)用實(shí)體都轉(zhuǎn)換成std::function類(lèi)型

  • 示例
    定義格式std::function<函數(shù)類(lèi)型>

bool greater_global(int arg1, int arg2) { return arg1 > arg2; }   // 普通函數(shù)

struct number {
  bool greater_member(int arg1, int arg2) { return arg1 > arg2; }  // 類(lèi)成員函數(shù)
  static bool greater_static(int arg1, int arg2) { return arg1 > arg2; } // 類(lèi)靜態(tài)函數(shù)
};

// 仿函數(shù)
struct greater_functor { 
  bool operator()(int arg1, int  arg2) const noexcept { return arg1> arg2; }
};

auto greater_lambda = [] (int arg1, int arg2) { return arg1 > arg2; };  // lambda
auto greater_lambda2 = [] (auto arg1, auto arg2) { return arg1 > arg2; };  // 通用 lambda

int main() {
  std::function<bool(int, int)> test_function;
  test_function = greater_global;

  number object;
  test_function = std::bind(&number::greater_member, &object, 
                            std::placeholders::_1, std::placeholders::_2);
  test_function = std::bind(&number::greater_static, 
                            std::placeholders::_1, std::placeholders::_2);

  test_function = greater_lambda ;
  test_function = greater_lambda2;
  test_function = greater_functor();
  
  test_function(3, 2);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末济丘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子洽蛀,更是在濱河造成了極大的恐慌摹迷,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件郊供,死亡現(xiàn)場(chǎng)離奇詭異峡碉,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)驮审,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)鲫寄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)吉执,“玉大人,你說(shuō)我怎么就攤上這事塔拳∈笾ぃ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵靠抑,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我适掰,道長(zhǎng)颂碧,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任类浪,我火速辦了婚禮载城,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘费就。我一直安慰自己诉瓦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布力细。 她就那樣靜靜地躺著睬澡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪眠蚂。 梳的紋絲不亂的頭發(fā)上煞聪,一...
    開(kāi)封第一講書(shū)人閱讀 49,764評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音逝慧,去河邊找鬼昔脯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛笛臣,可吹牛的內(nèi)容都是我干的云稚。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼沈堡,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼静陈!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起踱蛀,我...
    開(kāi)封第一講書(shū)人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤窿给,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后率拒,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體崩泡,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年猬膨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了角撞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呛伴。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖谒所,靈堂內(nèi)的尸體忽然破棺而出热康,到底是詐尸還是另有隱情,我是刑警寧澤劣领,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布姐军,位于F島的核電站,受9級(jí)特大地震影響尖淘,放射性物質(zhì)發(fā)生泄漏奕锌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一村生、第九天 我趴在偏房一處隱蔽的房頂上張望惊暴。 院中可真熱鬧,春花似錦趁桃、人聲如沸辽话。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)油啤。三九已至,卻和暖如春忽肛,著一層夾襖步出監(jiān)牢的瞬間村砂,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工屹逛, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留础废,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓罕模,卻偏偏與公主長(zhǎng)得像评腺,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子淑掌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容