日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

lambda 和 std::function

發布時間:2024/4/18 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 lambda 和 std::function 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. Lambda函數的用處

lambda 表達式可以方便地構造匿名函數,如果你的代碼里面存在大量的小函數,而這些函數一般只被調用一次,那么不妨將他們重構成 lambda 表達式。

C++11 的 lambda 表達式規范如下:

[ capture ] ( params ) mutable exception attribute -> ret { body }(1)
[ capture ] ( params ) -> ret { body }(2)
[ capture ] ( params ) { body }(3)
[ capture ] { body }(4)

其中

  • (1) 是完整的 lambda 表達式形式,

  • (2) const 類型的 lambda 表達式,該類型的表達式不能修改捕獲("capture")列表中的值。

  • (3)省略了返回值類型的 lambda 表達式,但是該 lambda 表達式的返回類型可以按照下列規則推演出來:

    • 如果 lambda 代碼塊中包含了 return 語句,則該 lambda 表達式的返回類型由 return 語句的返回類型確定。

    • 如果沒有 return 語句,則類似 void f(...) 函數。

  • 省略了參數列表,類似于無參函數 f()。

mutable 修飾符說明 lambda 表達式體內的代碼可以修改被捕獲的變量,并且可以訪問被捕獲對象的 non-const 方法。

exception 說明 lambda 表達式是否拋出異常(noexcept),以及拋出何種異常,類似于void f() throw(X, Y)。

attribute 用來聲明屬性。

另外,capture 指定了在可見域范圍內 lambda 表達式的代碼內可見得外部變量的列表(詳見第三節),具體解釋如下:

  • [a,&b] a變量以值的方式被捕獲,b以引用的方式被捕獲。

  • [this] 以值的方式捕獲 this 指針。

  • [&] 以引用的方式捕獲所有的外部自動變量。

  • [=] 以值的方式捕獲所有的外部自動變量。

  • [] 不捕獲外部的任何變量。

此外,params 指定 lambda 表達式的參數。

2. Lambda函數中的變量截取

捕獲:

(1)值捕獲

與參數傳值異同,

相同點:值捕獲的前提是變量可以拷貝

不同點:被捕獲的變量在lambda被創建時拷貝,而非調用時才拷貝

void f(const int*);void g(){ const int N = 10; [=]{ int arr[N]; // not an odr-use: refers to g's const int N f(&N); // odr-use: causes N to be captured (by copy) // &N is the address of the closure object's member N, not g's N }();}

(2)引用捕獲

與引用傳參類似,引用捕獲保存的是引用,值會發生變化。

#include <iostream> auto make_function(int& x) { return [&]{ std::cout << x << '\n'; };//return [=]{ std::cout << x << '\n'; }; //值捕獲打印3,創建時傳入的值是多少就是多少 } int main() { int i = 3; auto f = make_function(i); // the use of x in f binds directly to i i = 5; f(); // OK; prints 5}

(3)隱式捕獲

[] 空捕獲列表

[name1, name2, ...] 捕獲一系列變量

[&] 引用捕獲, 讓編譯器自行推導捕獲列表

[=] 值捕獲, 讓編譯器執行推導應用列表

void f3() { float x, &r = x; [=] { // x and r are not captured (appearance in a decltype operand is not an odr-use) decltype(x) y1; // y1 has type float decltype((x)) y2 = y1; // y2 has type float const& because this lambda // is not mutable and x is an lvalue decltype(r) r1 = y1; // r1 has type float& (transformation not considered) decltype((r)) r2 = y2; // r2 has type float const& };}

當然C++14,C++17,C++20對lambda做了更多的更新,這里就不多講了。

4. Lambda函數和STL?

lambda函數的引入為STL的使用提供了極大的方便。比如下面這個例子,當你想便利一個vector的時候,原來你得這么寫:

vector<int> v; v.push_back( 1 ); v.push_back( 2 ); //... for ( auto itr = v.begin(), end = v.end(); itr != end; itr++ ) { cout << *itr; }

現在有了lambda函數你就可以這么寫:

vector<int> v; v.push_back( 1 ); v.push_back( 2 ); //... for_each( v.begin(), v.end(), [] (int val) { cout << val; } );

而且這么寫了之后執行效率反而提高了。因為編譯器有可能使用”循環展開“來加速執行過程。

?

仿函數(functor)

什么是仿函數(functor)

????functor的英文解釋為something that performs a function,即其行為類似函數的東西。C++中的仿函數是通過在類中重載()運算符實現,使你可以像使用函數一樣來創建類的對象。

仿函數(functor)的實現及使用

//?this?is?a?functor struct?add_x?{add_x(int?x)?:?x(x)?{}int?operator()(int?y)?{?return?x?+?y;?} private:int?x; }; //?usage: add_x?add42(42);?//?create?an?instance?of?the?functor?class int?i?=?add42(8);?//?and?"call"?it assert(i?==?50);?//?and?it?added?42?to?its?argument std::vector<int>?in;?//?assume?this?contains?a?bunch?of?values) std::vector<int>?out; //?Pass?a?functor?to?std::transform,?which?calls?the?functor?on?every?element? //?in?the?input?sequence,?and?stores?the?result?to?the?output?sequence std::transform(in.begin(),?in.end(),?out.begin(),?add_x(1));? assert(out[i]?==?in[i]?+?1);?//?for?all?i

為什么使用仿函數(functor)

  • 迭代和計算邏輯分離

????使用仿函數可以使迭代和計算分離開來。因而你的functor可以應用于不同場合,在STL的算法中就大量使用了functor,下面是STL中for_each中使用functor的示例:

struct?sum {sum(int?*?t):total(t){};int?*?total;void?operator()(int?element){*total+=element;} }; int?main() {int?total?=?0;sum?s(&total);int?arr[]?=?{0,?1,?2,?3,?4,?5};std::for_each(arr,?arr+6,?s);cout?<<?total?<<?endl;?//?prints?total?=?15; }
  • 參數可設置

????可以很容易通過給仿函數(functor)設置參數,來實現原本函數指針才能實現的功能,看下面代碼:

class?CalculateAverageOfPowers { public:CalculateAverageOfPowers(float?p)?:?acc(0),?n(0),?p(p)?{}void?operator()?(float?x)?{?acc?+=?pow(x,?p);?n++;?}float?getAverage()?const?{?return?acc?/?n;?} private:float?acc;int???n;float?p; };

????這個仿函數的功能是求給定值平方或立方運算的平均值。只需要這樣來聲明一個對象即可:

CalculateAverageOfPowers?my_cal(2);
  • 有狀態

????與普通函數另一個區別是仿函數(functor)是有狀態的,所以可以進行諸如下面這種操作:

CalculateAverage?avg; avg?=?std::for_each(dataA.begin(),?dataA.end(),?avg); avg?=?std::for_each(dataB.begin(),?dataB.end(),?avg); avg?=?std::for_each(dataC.begin(),?dataC.end(),?avg);

????對多個不同的數據集進行取平均。

  • 性能

????我們看一下2中寫的代碼:

std::transform(in.begin(),?in.end(),?out.begin(),?add_x(1));

????編譯器可以準確知道std::transform需要調用哪個函數(add_x::operator)。這意味著它可以內聯這個函數調用。而如果使用函數指針,編譯器不能直接確定指針指向的函數,而這必須在程序運行時才能得到并調用。

????一個例子就是比較std::sort 和qsort ,STL的版本一般要快5-10倍

  • 總結

????當然,前3點都可以使用傳統的函數和指針實現,但是用仿函數(functor)可以讓這種實現變的更加簡單。

?

函數對象包裝器之std::function

std::function? ?是C++11中又一個新的概念。想想在之前的C++中,function一直是一個尷尬的存在,它作為程序的一部分,被“釘死”在程序的代碼段,而且可調用的對象五花八門。

C++11打破了這個尷尬,統一可調用對象的概念,可調用對象有以下幾種定義:

  • 是一個函數指針,參考 C++ 函數指針和函數類型;

  • 是一個具有operator()成員函數的類的對象;

  • 可被轉換成函數指針的類對象;

  • 一個類成員函數指針;

C++中可調用對象的雖然都有一個比較統一的操作形式,但是定義方法五花八門,這樣就導致使用統一的方式保存可調用對象或者傳遞可調用對象時,會十分繁瑣。C++11中提供了std::function和std::bind統一了可調用對象的各種操作

舉個例子:

// 普通函數int add(int a, int b){return a+b;} // lambda表達式auto mod = [](int a, int b){ return a % b;} // 函數對象類struct divide{ int operator()(int denominator, int divisor){ return denominator/divisor; }};

上述三種可調用對象雖然類型不同,但是共享了一種調用形式:

int(int ,int)

std::function就可以將上述類型保存起來,如下:

std::function<int(int ,int)> a = add; std::function<int(int ,int)> b = mod ; std::function<int(int ,int)> c = divide();

總結一下:

std::function是一個類模版,是一種通用、多態的函數封裝。

std::function對象可以對普通函數、Lambda表達式、函數指針、以及其它函數對象等 進行存儲、復制、和調用操作。

std::function對象是對可調用實體類型安全的包裹(我們知道像函數指針這類可調用實體,是類型不安全的)。

std::function通常是一個函數對象類,它包裝其它任意的函數對象,被包裝的函數對象具有類型為T1, …,TN的N個參數,并且返回一個可轉換到R類型的值。

std::function使用 模板轉換構造函數接收被包裝的函數對象;特別是,閉包類型可以隱式地轉換為std::function。

最簡單的理解就是:

通過std::function對可調用實體(普通函數、Lambda表達式、函數指針、以及其它函數對象等)的封裝,形成一個新的可調用的std::function對象;讓我們不再糾結那么多的可調用實體

下邊貼段代碼加深一下理解:

#include <functional>#include <iostream>using namespace std; std::function< int(int) > Functional; // 普通函數int TestFunc(int a){ return a;} // Lambda表達式auto lambda = [](int a)->int{ return a; }; // 仿函數(functor)class Functor{public: int operator()(int a){ return a; }}; // 1.類成員函數// 2.類靜態函數class TestClass{public: int ClassMember(int a) { return a; } static int StaticMember(int a) { return a; }}; int main(){ // 普通函數 Functional = TestFunc; int result = Functional(10); cout << "普通函數:"<< result << endl; // Lambda表達式 Functional = lambda; result = Functional(20); cout << "Lambda表達式:"<< result << endl; // 仿函數 Functor testFunctor; Functional = testFunctor; result = Functional(30); cout << "仿函數:"<< result << endl; // 類成員函數 TestClass testObj; Functional = std::bind(&TestClass::ClassMember, testObj, std::placeholders::_1); result = Functional(40); cout << "類成員函數:"<< result << endl; // 類靜態函數 Functional = TestClass::StaticMember; result = Functional(50); cout << "類靜態函數:"<< result << endl; return 0;}

總結

以上是生活随笔為你收集整理的lambda 和 std::function的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。