C++ lambda和function
lambda表示式
lambda表示式又稱為匿名錶達式,是C11提出的新語法。[]儲存lambda表示式要捕獲的值,()內的引數為形參,可供外部呼叫傳值。lambda表示式可以直接呼叫
// 1 匿名呼叫
[](string name)
{
cout << "this is anonymous" << endl;
cout << "hello " << name << endl;
}("zack");
上述程式碼定義了一個匿名函式後直接呼叫。我們可以通過auto初始化一個變數儲存lambda表示式
// 2 通過auto賦值
auto fname = [](string name)
{
cout << "this is auto " << endl;
cout << "hello " << name << endl;
};
fname("Rolin");
通過auto定義fname,然後儲存了lambda表示式,之後呼叫fname即可。也可以通過函式指標的方式接受lambda表示式
typedef void (*P_NameFunc)(string name); // 3 函式指標 P_NameFunc fname2 = [](string name) { cout << "this is P_NameFunc " << endl; cout << "hello " << name << endl; }; fname2("Vivo");
P_NameFunc定義了fname2函式指標接受了lambda表示式。也可以通過function物件接受lambda表示式,function類是C11新增的語法。
// 4 function function<void(string)> funcName; funcName = [](string name) { cout << "this is function " << endl; cout << "hello " << name << endl; }; funcName("Uncle Wang");
用一個function物件接受了lambda表示式,同樣可以呼叫該function物件funcName達到呼叫lambda的效果。
談談lambda的捕獲
1 值捕獲
int age = 33;
string name = "zack";
int score = 100;
string job = "softengineer";
//值捕獲
[age, name](string name_)
{
cout << "age is " << age << " name is " << name << " self-name is " << name_ << endl;
}("Novia");
上述lambda表示式捕獲了age和name,是以值的方式來捕獲的。所以無法在lambda表示式內部修改age和name的值,如果修改age和name,編譯器會報錯,提示無法修改const常量,因為age和name是以值的方式被捕獲的。
2 引用捕獲
int age = 33;
string name = "zack";
int score = 100;
string job = "softengineer";
[&age, &name](string name_)
{
cout << "age is " << age << " name is " << name << " self-name is " << name_ << endl;
name = "Xiao Li";
age = 18;
}("Novia");
[]裡age和name前邊添加了&,此時age和name是以引用方式捕獲的。所以可以在lambda表示式中修改age和name的值。
C++的lambda表示式雖然可以捕獲區域性變數的引用,達到類似閉包的效果,但不是真的閉包,golang和python等語言通過閉包捕獲區域性變數後可以增加區域性變數的宣告週期,C++無法做到這一點,所以下面的呼叫會出現崩潰。
vector<function<void(string)>> vec_Funcs;
void use_lambda2()
{
int age = 33;
string name = "zack";
int score = 100;
string job = "softengineer";
vec_Funcs.push_back([age, name](string name_)
{ cout << "this is value catch " << endl;
cout << "age is " << age << " name is " << name << " self-name is " << name_ << endl; });
//危險,不要捕獲區域性變數的引用
vec_Funcs.push_back([&age, &name](string name_)
{ cout << "this is referenc catch" << endl;
cout << "age is " << age << " name is " << name << " self-name is " << name_ << endl; });
}
void use_lambda3()
{
for (auto f : vec_Funcs)
{
f("zack");
}
}
int main(){
use_lambda2();
use_lambda3();
}
use_lambda2中將lambda表示式儲存在function型別的vector裡,當use_lambda2結束後,裡邊的區域性變數都被釋放了,而vector中的lambda表示式還儲存著區域性變數的引用,在呼叫use_lambda3時呼叫lambda表示式,此時訪問區域性變數已經被釋放了,所以導致程式崩潰。
3 全部用值捕獲,name用引用捕獲
int age = 33;
string name = "zack";
int score = 100;
string job = "softengineer";
[=, &name]()
{
cout << "age is " << age << " name is " << name << " score is " << score << " job is " << job << endl;
name = "Cui Hua";
}();
通過=表示所有變數都以值的方式捕獲,如果希望某個變數以引用方式捕獲則單獨在這個變數前加&。
4 全部用引用捕獲,只有name用值捕獲
int age = 33;
string name = "zack";
int score = 100;
string job = "softengineer";
[&, name]()
{
cout << "age is " << age << " name is " << name << " score is " << score << " job is " << job << endl;
}();
通過&方式表示所有變數都已引用方式捕獲,如果希望某個變數以值方式捕獲則單獨在這個變數前加=。
萬能的function
我們可以用function儲存形參和返回值相同的一類函式指標,可呼叫物件,lambda表示式等。
void use_function()
{
list<function<void(string)>> list_Funcs;
//儲存函式物件
list_Funcs.push_back(FuncObj());
//儲存lambda表示式
list_Funcs.push_back([](string str)
{ cout << "this is lambda call " << str << endl; });
//儲存全域性函式
list_Funcs.push_back(globalFun);
for (const auto &f : list_Funcs)
{
f("hello zack");
}
}
bind操作
C11同樣提供了bind操作,將原函式的幾個引數通過bind繫結傳值,返回一個新的可呼叫物件。
//繫結全域性函式
auto newfun1 = bind(globalFun2, placeholders::_1, placeholders::_2, 98, "worker");
//相當於呼叫globalFun2("Lily",22, 98,"worker");
newfun1("Lily", 22);
//多傳引數沒有用,相當於呼叫globalFun2("Lucy",28, 98,"worker");
newfun1("Lucy", 28, 100, "doctor");
auto newfun2 = bind(globalFun2, "zack", placeholders::_1, 100, placeholders::_2);
//相當於呼叫globalFun2("zack",33,100,"engineer");
newfun2(33, "engineer");
auto newfun3 = bind(globalFun2, "zack", placeholders::_2, 100, placeholders::_1);
newfun3("coder", 33);
placeholders表示佔位符,_1表示新生成函式的第一個引數, _2表示新生成函式的第二個引數,將這些引數傳遞給原函式達到佔位的效果,原函式的其餘引數通過bind繫結固定值。
接下來定義類
class BindTestClass
{
public:
BindTestClass(int num_, string name_) : num(num_), name(name_) {}
static void StaticFun(const string &str, int age);
void MemberFun(const string &job, int score);
public:
int num;
string name;
};
實現靜態函式和成員函式
void BindTestClass::StaticFun(const string &str, int age)
{
cout << "this is static function" << endl;
cout << "name is " << str << endl;
cout << "age is " << age << endl;
}
void BindTestClass::MemberFun(const string &job, int score)
{
cout << "this is member function" << endl;
cout << "name is " << name << endl;
cout << "age is " << num << endl;
cout << "job is " << job << endl;
cout << "score is " << score << endl;
}
我們通過bind繫結靜態成員函式
//繫結類的靜態成員函式,加不加&都可以
// auto staticbind = bind(BindTestClass::StaticFun, placeholders::_1, 33);
auto staticbind = bind(&BindTestClass::StaticFun, placeholders::_1, 33);
staticbind("zack");
新生成的staticbind函式可以直接傳遞一個引數zack就完成了呼叫。接下來用bind繫結成員函式
BindTestClass bindTestClass(33, "zack");
// 繫結類的成員函式,一定要傳遞物件給bind的第二個引數,可以是類物件,也可以是類物件的指標
// 如果要修改類成員,必須傳遞類物件的指標
auto memberbind = bind(BindTestClass::MemberFun, &bindTestClass, placeholders::_1, placeholders::_2);
memberbind("coder", 100);
auto memberbind2 = bind(BindTestClass::MemberFun, placeholders::_3, placeholders::_1, placeholders::_2);
memberbind2("coder", 100, &bindTestClass);
//繫結類成員時,物件必須取地址
auto numbind = bind(&BindTestClass::num, placeholders::_1);
std::cout << numbind(bindTestClass) << endl;
當然也可以直接用function物件接受bind返回的結果
// function接受bind返回的函式
function<void(int, string)> funcbind = bind(globalFun2, "zack", placeholders::_1, 100, placeholders::_2);
funcbind(33, "engineer");
// function接受bind 成員函式
function<void(string, int)> funcbind2 = bind(BindTestClass::MemberFun, &bindTestClass, placeholders::_1, placeholders::_2);
funcbind2("docker", 100);
function<void(string, int, BindTestClass *)> funcbind3 = bind(BindTestClass::MemberFun, placeholders::_3, placeholders::_1, placeholders::_2);
funcbind3("driver", 100, &bindTestClass);
// function 直接接受成員函式,function的模板列表裡第一個引數是類物件引用
function<void(BindTestClass &, const string &, int)> functomem = BindTestClass::MemberFun;
functomem(bindTestClass, "functomem", 88);
// function 繫結類的靜態成員函式
function<void(const string &)> funbindstatic = bind(&BindTestClass::StaticFun, placeholders::_1, 33);
funbindstatic("Rolis");
lambda和bind的使用就介紹到這裡
原始碼連結:https://gitee.com/secondtonone1/cpplearn
視訊連結: https://www.bilibili.com/video/BV15S4y1Y7no