【C++】Lamda表示式、仿函式(函式物件)
阿新 • 來源:網路 • 發佈:2023-01-23
Lambda表示式的引入
有的函式物件類只用來定義一個物件,而且這個物件只使用了一次,這樣的函式物件類有點浪費。而且,定義函式物件類的地方和使用函式物件類可能相隔較遠,想要檢視其operator()成員函式到底做了什麼會比較麻煩。
對於只使用一次的函式物件類,能否直接在使用它的地方定義呢?
Lambda表示式就能解決這個問題,使用Lambda表示式可以定義並建立匿名的函式物件,減少程式中函式物件類的數量,以簡化程式設計工作。
Lambda的語法格式如下:

【捕獲列表】捕獲上下文的變數以供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();
}

可變規格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);
}
