(給CPP開發者加星標,提升C/C++技能)
英文:Vishal Chovatiya,翻譯:CPP開發者/張宗豪【導讀】:Lambda函數是 Modern C++在C++11中的一個體現 ,雖然在網絡上已經有很多關於lambda函數的討論或者教學,但是仍然有一部分內容(例如IIFE,lambda類型等)沒人談論。因此在這裡,我不僅要向您展示C++中的lambda函數,而且還將介紹Lambda的工作原理以及發展。
以下是正文
什麼是lambda函數?
本文標題有點誤導。因為 lambda並不總是函數指針,它一個表達式。但是為了簡單起見,我一直都稱它為函數。那麼在本文中,我將會交替使用lambda函數和表達式來稱呼。
Lambda函數是一段簡短的代碼片段,它具有以下特點:
(1)不值得命名(無名,匿名,可處理等,無論您如何稱呼)
(2)不會重複使用
換句話說,它只是語法糖。lambda函數的語法定義為:
[ capture list ] (parameters) -> return-type { method definition}通常, 編譯器會評估lambda函數本身的返回類型。因此,我們不需要顯式指定返回類型,即 -> return-type,但是在某些複雜的情況下,編譯器無法推斷出返回類型,此時我們需要指定返回類型。
為什麼要使用lambda函數?
C++包括許多有用的通用函數,例如 std::for_each,通常情況下方便了我們的使用。但是,當需要考慮特殊需求的時候,就比較頭疼了。如下代碼所示:
struct print{ void operator()(int element){ cout <<element << endl; }};int main(void){ std::vector<int> v = {1, 2, 3, 4, 5}; std::for_each(v.begin(),v.end(), print()); return 0;}如果您只在某個特定位置使用一次print,卻寫了一個類。這樣的操作代價似乎太大了。
但是,對於這種情況,內聯代碼將更合適,並且可以通過如下的lambda函數來實現:
std::for_each(v.begin(), v.end(), [](int element) { cout <<element << endl; });lambda函數工作原理
[&i] ( ) { std::cout << i; }
struct anonymous{ int &m_i; anonymous(int &i) :m_i(i) {} inline autooperator()()const{ std::cout << i; }};如代碼所示,編譯器為每一個lambda函數生成獨特的閉合。捕獲列表將成為閉包中的構造函數參數,如果按值捕獲,則會在閉包中創建相應類型的數據成員。此外,您可以在lambda函數參數中聲明變量/對象,它將成為調用運算符的參數,即operator()。
使用Lambda函數的好處
(1)零成本抽象。是的! 你沒看錯。lambda不會犧牲性能,運行速度和普通函數一樣快。
(2)此外,代碼會變得更加緊湊,結構層次更加明顯和代碼可讀性更佳。
學習lambda表達式
1.通過引用/值捕獲
int main(){ int x = 100, y = 200; auto print = [&]{ std::cout <<__PRETTY_FUNCTION__ << " : " << x<< " , " << y << std::endl; }; print(); return 0;}輸出:
main()::<lambda()> : 100 , 200在上面的示例中,我在捕獲列表中用到了 &。通過引用的方式捕獲變量 x和 y。同樣,= 表示按值捕獲,這將在閉包內創建相同類型的數據成員,同時賦上相同的值。需要注意的是,參數列表是可選的, 如果不將參數傳遞給lambda表達式,則可以省略空括號。
2.Lambda捕獲列表
2.1 將lambda作為參數傳遞
template <typename Functor>void f(Functor functor){ std::cout <<__PRETTY_FUNCTION__ << std::endl;}int g() { static int i = 0; return i++; }int main(){ auto lambda_func =[i = 0]() mutable { return i++; }; f(lambda_func); f(g); }輸出:
Function Type : void f(Functor) [with Functor = main()::<lambda(int)>]Function Type : void f(Functor) [with Functor = int (*)(int)]您也可以將lambda函數作為參數傳遞給其他函數,就像我上面編寫的普通函數一樣。相信您注意到了,我在捕獲列表中聲明了變量i,它將成為數據成員。所以,每次調用lambda_func時,它將被返回並遞增。
2.2lambda捕獲this指針或成員變量
class Example{public: Example() : m_var(10) {} void func(){ [=]() { std::cout << m_var<< std::endl; }(); }private: int m_var;};int main(){ Example e; e.func();}捕獲this指針也可以使用 [this], [=]或者 [&]。在上述任何情況下,類內數據成員(包括 private)的訪問方式與常規方法一樣。
可以看到lambda表達式的末尾,我多寫了一個 ( ),該函數通常在聲明之後立刻對其進行調用。它稱為IIFE(立即調用函數表達式)。
C++ lambda函數類型
1.通用lambda
const auto l = [](auto a, auto b, auto c) {};struct anonymous{ template <class T0, class T1, class T2> auto operator()(T0 a, T1 b, T2 c) const { }};C++ 14中引入的通用lambda可以使用auto說明符。
2.可變參數通用λ
void print() {}template <typename First, typename... Rest>void print(const First &first, Rest &&... args){ std::cout << first<< std::endl; print(args...);}int main(){ auto variadic_generic_lambda = [](auto... param) { print(param...); }; variadic_generic_lambda(1, "lol", 1.1);}具有可變參數的Lambda在許多情況下非常有用,例如調試,使用不同的數據輸入重複操作等。
3.mutable lambda函數
通常,lambda的函數調用運算符是const-by-value,這意味著lambda需要捕獲可變值的 關鍵字時,需要使用mutable關鍵字。
[]() mutable {}
struct anonymous{ auto operator()() { }};4.Lambda作為函數指針
#include<iostream>#include<type_traits>
int main(){ auto funcPtr = +[] {}; static_assert(std::is_same<decltype(funcPtr), void (*)()>::value);}您可以通過添加命令「+」來強制編譯器將lambda生成為函數指針而不是閉包。
5.lambda函數作為返回值
const auto less_than = [](auto x) { return [x](auto y) { return y < x; };};
int main(void){ auto less_than_five = less_than(5); std::cout << less_than_five(3) << std::endl; std::cout << less_than_five(10) << std::endl; return 0;}再進一步,lambda函數還可以返回另一個lambda函數。這也為代碼的自定義性,代碼可讀性和緊湊性帶來無限可能。
6.constexpr lambda表達式
從C ++ 17開始,可以將lambda表達式聲明為 constexpr。
constexpr auto sum = [](const auto &a, const auto &b) { return a + b; };constexpr int answer = sum(10, 10);即使你沒有指定 constexpr,如果它恰好滿足所有 constexpr函數的要求,那麼它也會被聲明為constexpr。
結束語
希望您喜歡這篇文章。文中我使用幾個簡單的小例子來介紹關於lambda的大量複雜問題。考慮到代碼的可表達性和易維護性,無論您想到什麼,都應該使用lambda,就像可以在自定義刪除器中將其用於智能指針和大多數STL算法一樣。
https://hackernoon.com/all-about-lambda-functions-in-cfrom-c11-to-c17-2t1j32qw
- EOF -
關於C++lambda,歡迎在評論中和我探討。覺得文章不錯,請點讚和在看支持我繼續分享好文。謝謝!
關注『CPP開發者』
看精選C++技術文章 . 加C++開發者專屬圈子
↓↓↓
點讚和在看就是最大的支持❤️