1. 程式人生 >實用技巧 >其它 >【C++】Lamda表示式、仿函式(函式物件)

【C++】Lamda表示式、仿函式(函式物件)


Lambda表示式的引入

有的函式物件類只用來定義一個物件,而且這個物件只使用了一次,這樣的函式物件類有點浪費。而且,定義函式物件類的地方和使用函式物件類可能相隔較遠,想要檢視其operator()成員函式到底做了什麼會比較麻煩。

對於只使用一次的函式物件類,能否直接在使用它的地方定義呢?

Lambda表示式就能解決這個問題,使用Lambda表示式可以定義並建立匿名的函式物件,減少程式中函式物件類的數量,以簡化程式設計工作。

Lambda的語法格式如下:

【C++】Lamda表示式、仿函式(函式物件)

【捕獲列表】捕獲上下文的變數以供Lambda函式使用。

【引數列表】如果不需要引數傳遞,可以同"()"一起省略。

【mutable】可變規格。預設情況Lambda函式總是一個const函式,mutable可以取消其常量性。在使用該修飾符時,引數列表不可省略(即使沒有引數)。

【throw】異常宣告。用於指定函式丟擲的異常。該部分和mutable根據實際情況新增,可省略。

【->返回值型別】當返回值型別為void 或返回值型別明確的情況下,可以省略這部分,讓編譯器自動推斷返回值型別。

【函式體】不可省略,但函式體可以為空


例項演示

使用 lambda 表示式捕獲列表

  • [] 不捕獲任何變數。
int main()
{
    int a = 0, b = 1;
    auto f1 = [] { return a; };               // error,沒有捕獲外部變數
}
  • [&] 捕獲外部作用域中所有變數,並作為引用在函式體中使用(按引用捕獲)。
int main()
{
    int a = 0, b = 1;
    auto f2 = [&] { return a++; };            // OK,捕獲所有外部變數,並對a執行自加運算

    cout << f2() << endl;//0
    cout << a << endl;//1
}
  • [=] 捕獲外部作用域中所有變數,並作為副本在函式體中使用(按值捕獲)。
int main()
{
    int a = 0, b = 1;
    auto f3 = [=] { return a; };              // OK,捕獲所有外部變數,並返回a
    auto f4 = [=] { return a++; };            // error,a是以複製方式捕獲的,無法修改

    cout<<f3()<<endl;//0
    cout<<a<<endl;//0

}
  • [=,&foo] 按值捕獲外部作用域中所有變數,並按引用捕獲 foo 變數。
int main()
{
    int a = 0, b = 1;
    auto f7 = [=, &b] { return a + (b++); };  // OK,捕獲所有外部變數和b的引用,並對b做自加運算

    cout << f7() << endl;//1
    cout << a << endl;//0
    cout << b << endl;//2
}
  • [bar] 按值捕獲 bar 變數,同時不捕獲其他變數。
int main()
{
    int a = 0, b = 1;
    auto f5 = [a] { return a + b; };          // error,沒有捕獲變數b
}
  • [this] 捕獲當前類中的 this指標,讓 lambda 表示式擁有和當前類成員函式同樣的訪問許可權。如果已經使用了 & 或者 =,就預設新增此選項。捕獲 this 的目的是可以在 lamda 中使用當前類的成員函式和成員變數。
class A
{
public:
    int i_ = 0;
    int getvalue() { return i_; }
    void func(int x, int y)
    {
        auto x1 = [] { return i_; };                    // error,沒有捕獲外部變數
        auto x2 = [=] { return i_ + x + y; };           // OK,捕獲所有外部變數
        auto x3 = [&] { return i_ + x + y; };           // OK,捕獲所有外部變數
        auto x4 = [this] { return i_; };                // OK,捕獲this指標
        auto x5 = [this] { return i_ + x + y; };        // error,沒有捕獲x、y
        auto x6 = [this, x, y] { return i_ + x + y; };  // OK,捕獲this指標、x、y
        auto x7 = [this] { return i_++; };              // OK,捕獲this指標,並修改成員的值
        auto x8 = [this] { return getvalue(); };        // OK,捕獲this指標,訪問當前類的成員函式
    }
};

Lambda引數列表

int main()
{
    int a = 0, b = 1;
    //有參
    auto function1 = [](int first, int second) { return first + second; };
    cout << function1(a,b)<< endl;
    //無參
    auto function2 = [] { cout<<"hello world"<<endl; };
    function2();

}

【C++】Lamda表示式、仿函式(函式物件)

可變規格mutable

int main()
{
    int i = 100;
    //auto function1 = [i] { i = 10; }   // error,捕獲的i是const屬性
    auto function2 = [i]()mutable { i = 10; return i; };  // mutable修改了捕獲的i的const屬性,但i本身沒有被修改
    cout << function2() << endl;//10
    cout << i << endl;//100
}

三、Lambda表示式的實質

一個lambda表示式實質是生成一個匿名函式類物件,並在匿名函式類中過載operator()方法。

int main()
{
    auto print = [] {cout << "hello world" << endl; };
    print();
}

​ 編譯器會把上面這一句翻譯為下面的程式碼:

class printclass
{
public:
    void operator()()const
    {
        cout << "hello world" << endl;
    }
};

int main()
{
    //用類的建構函式建立物件,print此時就是一個函式物件
    auto print = printclass();
    print();
}

四、仿函式(函式物件)

過載函式呼叫操作符()的類,其物件稱為函式物件,函式物件呼叫過載的operator()的行為類似於函式,又稱仿函式。

class printclass
{
public:
    void operator()()const
    {
        cout << "hello world" << endl;
    }
};

int main()
{
    //print此時就是一個函式物件
    auto print = printclass();
    print();//函式物件呼叫過載()類似於函式,所以又稱仿函式
}

函式物件兩大亮點 :

1)函式物件可以包含狀態;

2)函式物件是一種型別,可以作為模板引數。


五、Lamdba表示式適用場景

① Lamdba表示式應用於STL演算法庫

#include<iostream>
#include<algorithm>
using namespace std;

int main()
{
	auto Mygreater = [](int a, int b) {return a < b; };
	int a[] = { 45,12,34,77,90,11,2,4,5,55 };
	sort(a, a + 10, Mygreater);
	for (int i = 0; i < 10; i++)
	{
		cout << a[i] << " ";
	}
		
}

②Lamdba表示式應用於函式指標與function

#include<iostream>
#include<algorithm>
#include <functional>
using namespace std;

int main()
{
	
	int x = 8, y = 9;
	auto add = [](int a, int b) { return a + b; };
	std::function<int(int ,int)> Add = [](int a, int b) { return a + b; };

	cout << "add: " << add(x, y) << endl;
	cout << "Add: " << Add(x, y) << endl;

}

③Lamdba表示式作為函式的入參

#include<iostream>
#include <functional>
using namespace std;

using FuncCallback = std::function<void(void)>;

void DataCallback(FuncCallback callback)
{
	std::cout << "Start FuncCallback!" << std::endl;
	callback();
	std::cout << "End FuncCallback!" << std::endl;
}

auto callback_handler = [&]() {
	std::cout << "This is callback_handler"<<endl;
};

int main()
{
	
	DataCallback(callback_handler);
	
}