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);
}