迷茫2017-04-17 14:37:30
題目問的其它語言,這個太廣泛了,因為程式語言這麼多,有形如C語言這種傳統的函數,也有形如java那樣無法獨立存在的函數,還有各類腳本語言的不同形式的函數。
不過在函數是第一等公民的語言裡,要求函數能做到
函數可以獨立存在,且可在任意地方執行
函數的作用域是詞法作用域,且可捕捉外部變數
函數可從另一個函數傳回,也可以作為別的函數的參數(高階函數)
函數從別的函數生成後,所引用的作用域依舊可訪問,而不是被銷毀
在不少腳本語言和新興語言裡,函數大都是有以上特性表現的。
關於不同語言閉包捕捉的問題,可以參考我之前寫過的一篇文章 https://zhuanlan.zhihu.com/p/...
再說C++,C++有四個可以用來呼叫的東西
函數
函數指標
重載了()運算子的類別
lambda
這四個東西的類型各不相同,但是都可以做到執行函數,使用上都是形如func()
。
函數和函數指標在某個情況下是可以互轉的,它在C語言裡就存在,但是它存在一些難以解決的問題,包括
函數型難寫,難讀
函數內部無法保存狀態,沒有辦法實現外層變數擷取
重載了()運算子的類別是C++提供的一個仿函式寫法,它可以實作不少做法
class Test{
public:
Test(int a):_a(a){}
int operator ()(int b){
return _a+b;
}
private:
int _a;
};
使用上只要這樣:
Test sum1 = Test(1);
std::cout << sum1(10) << '\n';
Test sum2 = Test(10);
std::cout << sum2(10) << '\n';
可以看到sum1和sum2的內部都保存了一個狀態,進而表現不一,這是普通的函數和函數指標無法做到的。
然而重載了()運算子的類別的一大缺點就是不夠輕量級,不管是佔用空間還是執行效率上和函數/函數指標都存在差距。
C++11引進了lambda,典型的lambda是這樣
int a = 10;
auto func = [&a](int b){return a+b;};
std::cout << func(10) << '\n';
a = 20;
std::cout << func(10) << '\n';
可以看到lambda可以輕鬆捕捉變量,實現閉包效果,所以我們在很多場合下可以使用lambda來代替重載了()運算符的類別。
不過lambda也有局限,首先是它的類型是無法手動寫出來的,也就是說對於同樣的兩個lambda,它的類型是不同的
auto func = [&a](int b){return a+b;};
auto func2 = [&a](int b){return a+b;};
std::cout << (typeid(func).name() != typeid(func2).name()) << '\n';
所以我們只能定義的時候只能用auto,而且如果我們想把lambda存到vector、list等容器裡,就必須要用包裝類std::function,它本質上是個泛型的模板類,也同樣重載了()操作符
std::function<int(int)> func3 = func;
使用std::function之後,lambda的輕量優勢就消失了,不過有的時候也得這麼做。