網站首頁 編程語言 正文
Lambda 表達式
Lambda 表達式是現代 C++ 中最重要的特性之一,而 Lambda 表達式,實際上就是提供了一個類似匿名函數的特性, 而匿名函數則是在需要一個函數,但是又不想費力去命名一個函數的情況下去使用的。這樣的場景其實有很多很多, 所以匿名函數幾乎是現代編程語言的標配。
基礎
Lambda 表達式的基本語法如下:
[捕獲列表](參數列表) mutable(可選) 異常屬性 -> 返回類型 {
// 函數體
}
上面的語法規則除了 [捕獲列表] 內的東西外,其他部分都很好理解,只是一般函數的函數名被略去, 返回值使用了一個 -> 的形式進行(我們在上一節前面的尾返回類型已經提到過這種寫法了)。
所謂捕獲列表,其實可以理解為參數的一種類型,Lambda 表達式內部函數體在默認情況下是不能夠使用函數體外部的變量的, 這時候捕獲列表可以起到傳遞外部數據的作用。根據傳遞的行為,捕獲列表也分為以下幾種:
1. 值捕獲
與參數傳值類似,值捕獲的前提是變量可以拷貝,不同之處則在于,被捕獲的變量在 Lambda 表達式被創建時拷貝, 而非調用時才拷貝:
void lambda_value_capture() {
int value = 1;
auto copy_value = [value] {
return value;
};
value = 100;
auto stored_value = copy_value();
std::cout << "stored_value = " << stored_value << std::endl;
// 這時, stored_value == 1, 而 value == 100.
// 因為 copy_value 在創建時就保存了一份 value 的拷貝
}
2. 引用捕獲
與引用傳參類似,引用捕獲保存的是引用,值會發生變化。
void lambda_reference_capture() {
int value = 1;
auto copy_value = [&value] {
return value;
};
value = 100;
auto stored_value = copy_value();
std::cout << "stored_value = " << stored_value << std::endl;
// 這時, stored_value == 100, value == 100.
// 因為 copy_value 保存的是引用
}
3. 隱式捕獲
手動書寫捕獲列表有時候是非常復雜的,這種機械性的工作可以交給編譯器來處理,這時候可以在捕獲列表中寫一個 & 或 = 向編譯器聲明采用引用捕獲或者值捕獲.
總結一下,捕獲提供了 Lambda 表達式對外部值進行使用的功能,捕獲列表的最常用的四種形式可以是:
a. [] 空捕獲列表
b. [name1, name2, ...] 捕獲一系列變量
c. [&] 引用捕獲, 讓編譯器自行推導引用列表
d. [=] 值捕獲, 讓編譯器自行推導值捕獲列表
4. 表達式捕獲
這部分內容需要了解后面馬上要提到的右值引用以及智能指針
上面提到的值捕獲、引用捕獲都是已經在外層作用域聲明的變量,因此這些捕獲方式捕獲的均為左值,而不能捕獲右值。
C++14 給與了我們方便,允許捕獲的成員用任意的表達式進行初始化,這就允許了右值的捕獲, 被聲明的捕獲變量類型會根據表達式進行判斷,判斷方式與使用 auto 本質上是相同的:
#include <iostream>
#include <memory> // std::make_unique
#include <utility> // std::move
void lambda_expression_capture() {
auto important = std::make_unique<int>(1);
auto add = [v1 = 1, v2 = std::move(important)](int x, int y) -> int {
return x+y+v1+(*v2);
};
std::cout << add(3,4) << std::endl;
}
在上面的代碼中,important 是一個獨占指針,是不能夠被 "=" 值捕獲到,這時候我們可以將其轉移為右值,在表達式中初始化。
泛型 Lambda
上一節中我們提到了 auto 關鍵字不能夠用在參數表里,這是因為這樣的寫法會與模板的功能產生沖突。 但是 Lambda 表達式并不是普通函數,所以在沒有明確指明參數表類型的情況下,Lambda 表達式并不能夠模板化。 幸運的是,這種麻煩只存在于 C++11 中,從 C++14 開始,Lambda 函數的形式參數可以使用 auto 關鍵字來產生意義上的泛型:
auto add = [](auto x, auto y) {
return x+y;
};
add(1, 2);
add(1.1, 2.2);
函數對象包裝器
這部分內容雖然屬于標準庫的一部分,但是從本質上來看,它卻增強了 C++ 語言運行時的能力, 這部分內容也相當重要,所以放到這里來進行介紹。
std::function
Lambda 表達式的本質是一個和函數對象類型相似的類類型(稱為閉包類型)的對象(稱為閉包對象), 當 Lambda 表達式的捕獲列表為空時,閉包對象還能夠轉換為函數指針值進行傳遞,例如:
#include <iostream>
using foo = void(int); // 定義函數類型, using 的使用見上一節中的別名語法
void functional(foo f) { // 參數列表中定義的函數類型 foo 被視為退化后的函數指針類型 foo*
f(1); // 通過函數指針調用函數
}
int main() {
auto f = [](int value) {
std::cout << value << std::endl;
};
functional(f); // 傳遞閉包對象,隱式轉換為 foo* 類型的函數指針值
f(1); // lambda 表達式調用
return 0;
}
上面的代碼給出了兩種不同的調用形式,一種是將 Lambda 作為函數類型傳遞進行調用, 而另一種則是直接調用 Lambda 表達式,在 C++11 中,統一了這些概念,將能夠被調用的對象的類型, 統一稱之為可調用類型。而這種類型,便是通過 std::function 引入的。
C++11 std::function 是一種通用、多態的函數封裝, 它的實例可以對任何可以調用的目標實體進行存儲、復制和調用操作, 它也是對 C++ 中現有的可調用實體的一種類型安全的包裹(相對來說,函數指針的調用不是類型安全的), 換句話說,就是函數的容器。當我們有了函數的容器之后便能夠更加方便的將函數、函數指針作為對象進行處理。 例如:
#include <functional>
#include <iostream>
int foo(int para) {
return para;
}
int main() {
// std::function 包裝了一個返回值為 int, 參數為 int 的函數
std::function<int(int)> func = foo;
int important = 10;
std::function<int(int)> func2 = [&](int value) -> int {
return 1+value+important;
};
std::cout << func(10) << std::endl;
std::cout << func2(10) << std::endl;
}
std::bind 和 std::placeholder
而 std::bind 則是用來綁定函數調用的參數的, 它解決的需求是我們有時候可能并不一定能夠一次性獲得調用某個函數的全部參數,通過這個函數, 我們可以將部分調用參數提前綁定到函數身上成為一個新的對象,然后在參數齊全后,完成調用。 例如:
int foo(int a, int b, int c) {
;
}
int main() {
// 將參數1,2綁定到函數 foo 上,
// 但使用 std::placeholders::_1 來對第一個參數進行占位
auto bindFoo = std::bind(foo, std::placeholders::_1, 1,2);
// 這時調用 bindFoo 時,只需要提供第一個參數即可
bindFoo(1);
}
提示:注意 auto 關鍵字的妙用。有時候我們可能不太熟悉一個函數的返回值類型, 但是我們卻可以通過 auto 的使用來規避這一問題的出現。
原文鏈接:https://blog.csdn.net/scott198510/article/details/128991892
相關推薦
- 2022-08-02 C#?HttpClient?Post參數同時上傳文件的實現_C#教程
- 2022-04-11 關于指令重排現象的兩個階段詳解_相關技巧
- 2022-11-20 golang?實現?pdf?轉高清晰度?jpeg的處理方法_Golang
- 2022-09-26 RNN的手動推導與代碼逐行實現
- 2023-03-01 Android使用AndroidUtilCode實現多語言_Android
- 2022-03-14 Feign客戶端消費服務超時:com.netflix.hystrix.exception.Hystr
- 2022-08-10 pandas.DataFrame.iloc的具體使用詳解_python
- 2023-07-13 替換字符串中的任意字符及正則隱藏手機號中間四位
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支