(原創)C++11改進我們的程序之簡化我們的程序(六)


  這次要講的內容是: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,歡迎大家來交流技術。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM