這次要講的內容是:c++11中的lamda表達式。
lamda表達式是我最喜歡的一個c++11特性之一,在我的代碼中隨處可見它的身影,其實在c#3.5中就引入了lamda,java中至今還沒引入,要等明年的java8中才有lamda表達式,lamda來源於函數式編程的概念,也是現代編程語言的一個特點。c++11這次終於把lamda加進來了。也許有人要問,lamda有什么好處,為什么要加到c++中來?lamda表達式有這些優點:
- 聲明式編程風格:就地匿名定義你的目標函數或函數對象,不需要額外的寫一個命名函數或者函數對象。以更直接的方式去寫程序,好的可讀性和可維護性。
- 簡潔:不需要額外再寫一個函數或者函數對象,避免了代碼膨脹和功能分散,讓開發者更加集中精力在手邊的問題,同時也獲取了更高的生產率。
- 在需要的時間和地點實現謂詞,使我們的程序更靈活。
lamda表達式定義了一個匿名函數,並且可以捕獲一定范圍內的變量。lamda表達式的語法形式:
[ capture ] ( params ) -> ret { body } 如果沒有返回值也沒有參數,可以這樣簡寫: [](){ body }或者[]{ body } [] (int x) -> { return x; } auto fun = [](){cout<<"test"} fun();
lamda表達式可以截取一定范圍內的變量:
[] 不截取任何變量 [&} 截取外部作用域中所有變量,並作為引用在函數體中使用 [=] 截取外部作用域中所有變量,並拷貝一份在函數體中使用 [=, &foo] 截取外部作用域中所有變量,並拷貝一份在函數體中使用,但是對foo變量使用引用 [bar] 截取bar變量並且拷貝一份在函數體重使用,同時不截取其他變量 [this] 截取當前類中的this指針。如果已經使用了&或者=就默認添加此選項。截取this的目的是可以在lamda中使用當前類的成員函數和成員變量。
聲明式編程風格,簡潔的代碼
就地定義匿名函數,不再需要定義函數對象,大大簡化了標准庫算法的調用。比如c++11之前我們要調用for_each函數將vector中偶數打印出來,可能要這樣寫:
struct CountEven { CountEven(int& count) : m_count(count) { } void operator()(int val) { if(val/2==0) { m_count++; } } private: int m_count; }; int evenCount=0; for_each(v.begin(),v.end(),CountEven(evenCount)); cout<<"偶數有 "<<evenCount<<" 個"<<endl;
用lamda表達式寫:
for_each(v.begin(),v.end(), [&evenCount](const int& val) { if(val/2==0) { evenCount++; } });
不再需要定義函數對象,而且更簡潔明了,可讀性和維護性更好,開發效率也更高。
在需要的時間和地點實現謂詞,使我們的程序更靈活。
上一篇博文中的那個計算集合中大於5小於10的函數用Lamda更簡潔,之前用bind的寫法:
//查找大於10的元素的個數 int count = count_if(coll.begin(), coll.end(), bind1st(less<int>(), 10)); //查找小於10的元素的個數 int count = count_if(coll.begin(), coll.end(), bind2nd(less<int>(), 10)); //查找大於5小於10的元素的個數 auto f = bind(std::logical_and<bool>(), bind(std::greater<int>(),_1,5), bind(std::less_equal<int>(),_1,10)); int count = count_if(coll.begin(), coll.end(), f);
用lamda的寫法:
//查找大於10的元素的個數 int count = count_if(coll.begin(), coll.end(), [](int x){return x>10;}); //查找小於10的元素的個數 int count = count_if(coll.begin(), coll.end(), [](int x){return x<10;}); //查找大於5小於10的元素的個數 int count = count_if(coll.begin(), coll.end(), [](int x){return x>5&&x<10;});
孰優孰劣,一眼便知,這就是生產力啊。
lamda捕獲參數
stuct A { A():m_cout(0){} bool Add(int x, int y) { return x>y; } void TestLamda() { vector<int> v = {1,2,3,4}; sort(v.begin(), v.end(), [this](int x, int y){return Add(x,y);}); //調用成員函數,需要捕獲this才可以 int z = 4; sort(v.begin(), v.end(), [this,z](int x, int y) //z按值捕獲 { m_count = 4; //給成員變量賦值 return Add(x,y); }); //調用成員函數,需要捕獲this才可以 sort(v.begin(), v.end(), [this,&z](int x, int y) //z按引用捕獲 { z = count+3; //給z賦值 return Add(x,y); }); //以引用方式傳遞作用域內所有可見的局部變量(包括this) for_each(v.begin(), v.end(), [=](int x){cout<<z+x<<endl;}); //以引用方式傳遞作用域內所有可見的局部變量(包括this) for_each(v.begin(), v.end(), [&](int x){z++; cout<<z+x<<endl;}); //部分變量按引用傳遞 for_each(v.begin(), v.end(), [this, &z](int x){z++; cout<<z+x<<endl;}); } int m_cout; };
lamda能捕獲變量很有用,一些外面的參數可以根據需要按值或者引用傳入閉包。我甚至覺得lamda表達式幾乎可以取代function, 可以通過捕獲對象並調用對象的成員函數的方式取代bind成員函數的方式。例如:
用bind和function的寫法:
A a; auto f = std::bind(&A::Add, &a, std::placehoders::_1,std::placehoders::_2); int x = 1, y=2; auto ret = f(x, y);
用lamda的寫法:
auto f = [&a](int x, int y){return a.Add(x, y);}; auto ret = f(x, y);
效果是一樣的,代碼還更簡潔了。不過不能完全替代function的原因是lamda表達式不能作為成員變量,還有有些標准庫的和boost的方法不支持lamda,還需要function出場。
c++11引入函數式編程的概念中的lamda,讓我們的代碼更簡潔,更靈活,也更強大,並提高了開發效率,提高了可維護性。再次說一聲lamada真是個好東西!
c++11 boost技術交流群:296561497,歡迎大家來交流技術。