C++11右值引用與Lambda表達(dá)式的使用
C++11新特性
右值引用和移動(dòng)語(yǔ)義
傳統(tǒng)的C++語(yǔ)法中就有引用的語(yǔ)法,而C++11中新增了的右值引用語(yǔ)法特性,所以從現(xiàn)在開始我們之前學(xué)習(xí)的引用就叫做左值引用。無論左值引用還是右值引用,都是給對(duì)象取別名。那么什么是左值?什么是左值引用?
左值是一個(gè)表示數(shù)據(jù)的表達(dá)式(如變量名或解引用的指針),我們可以獲取它的地址+可以對(duì)它賦值,左值可以出現(xiàn)賦值符號(hào)的左邊,右值不能出現(xiàn)在賦值符號(hào)左邊。定義時(shí)const修飾符后的左值,不能給他賦值,但是可以取它的地址。左值引用就是給左值的引用,給左值取別名。
左值 / 右值
右值也是一個(gè)表示數(shù)據(jù)的表達(dá)式,如:字面常量、表達(dá)式返回值,函數(shù)返回值(這個(gè)不能是左值引用返回)等等,右值可以出現(xiàn)在賦值符號(hào)的右邊,但是不能出現(xiàn)出現(xiàn)在賦值符號(hào)的左邊,右值不能取地址。
右值引用就是對(duì)右值的引用,給右值取別名。
右值引用特性:
語(yǔ)法:Type&& 變量名= 值;
目的:
識(shí)別并利用臨時(shí)對(duì)象的資源:避免不必要的深拷貝。
實(shí)現(xiàn)移動(dòng)語(yǔ)義:轉(zhuǎn)移資源所有權(quán)而非復(fù)制。
完美轉(zhuǎn)發(fā):在模板中保持參數(shù)的值類別。
std::move():將左值轉(zhuǎn)為右值std::forward():完美轉(zhuǎn)發(fā)參數(shù)
左值:有名稱、有地址、可被取地址的持久對(duì)象
int main(){
int* p = new int(0); // 以下的p、b、c、*p都是左值
int b = 1; // b 是左值,1是右值
int *ptr=&b; // 可以取b的地址,說明b是左值
const int c = 2;
int*& rp = p; // 以下幾個(gè)是對(duì)上面左值的左值引用
int& rb = b;
const int& rc = c;
int& pvalue = *p;
return 0;}
右值 : 臨時(shí)對(duì)象、字面量、即將銷毀的對(duì)象
std::move(x); // 返回右值引用
double x = 1.1, y = 2.2;
10; // 字面量是右值
x + y; // 表達(dá)式結(jié)果是右值
fmin(x, y); //函數(shù)
// 以下幾個(gè)都是對(duì)右值的右值引用
int&& rr1 = 10; // 右值引用綁定到字面量(右值)
int& err = 42; // 錯(cuò)誤:左值引用不能直接綁定到右值
double&& rr2 = x + y;// x+y是右值(臨時(shí)計(jì)算結(jié)果)
double&& rr3 = fmin(x, y);
// 這里編譯會(huì)報(bào)錯(cuò):error C2106: “=”: 左操作數(shù)必須為左值
10 = 1;
x + y = 1;
fmin(x, y) = 1;
常見的左值和右值
左值示例:
- 變量名:
int x; 中的 x - 函數(shù)返回的左值引用:
std::getline()返回的std::istream& - 數(shù)組元素:
arr[0] - 解引用指針:
*ptr
右值示例:
- 字面量:
42,true - 臨時(shí)對(duì)象:
std::string("temp") - 表達(dá)式結(jié)果:
x + 1,func()(如果func()返回值類型) - 將亡值:
std::move(x)返回的右值引用

int a = 10; // 左值引用 引用 左值 int& ra = a; // 右值引用 引用 右值 int&& rr = 10;
需要注意的是右值是不能取地址的,但是給右值取別名后,會(huì)導(dǎo)致右值被存儲(chǔ)到特定位置,且可以取到該位置的地址,也就是說例如:不能取字面量10的地址,但是rr1引用后,可以對(duì)rr1取地址,也可以修改rr1。
如果不想rr1被修改,可以用const int&& rr1 去引用,是不是感覺很神奇,這個(gè)了解一下實(shí)際中右值引用的使用場(chǎng)景并不在于此,這個(gè)特性也不重要。
int main(){
double x = 1.1, y = 2.2;
int&& rr1 = 10;
const double&& rr2 = x + y;
rr1 = 20;
rr2 = 5.5; // 報(bào)錯(cuò)
return 0;
}
移動(dòng)語(yǔ)義
移動(dòng)語(yǔ)義允許將資源(如動(dòng)態(tài)分配的內(nèi)存)從一個(gè)對(duì)象轉(zhuǎn)移到另一個(gè)對(duì)象,而無需進(jìn)行深拷貝,從而顯著提高性能。
移動(dòng)構(gòu)造函數(shù)
移動(dòng)構(gòu)造函數(shù)接受一個(gè)右值引用作為參數(shù),用于從臨時(shí)對(duì)象中 “轉(zhuǎn)移” 資源。
// 移動(dòng)構(gòu)造函數(shù)
Date(Date&& other) noexcept
: year(other.year), month(other.month), day(other.day) {
other.year = nullptr; // 轉(zhuǎn)移資源所有權(quán)
std::cout << "移動(dòng)構(gòu)造: " << (year ? *year : 0)
<< "-" << month << "-" << day << std::endl;
}
移動(dòng)復(fù)制運(yùn)算符
移動(dòng)賦值運(yùn)算符用于從右值對(duì)象轉(zhuǎn)移資源。
// 移動(dòng)賦值運(yùn)算符
Date& operator=(Date&& other) noexcept {
if (this != &other) {
delete year; // 釋放當(dāng)前資源
year = other.year; // 轉(zhuǎn)移資源
month = other.month;
day = other.day;
other.year = nullptr; // 重置源對(duì)象
}
std::cout << "移動(dòng)賦值: " << (year ? *year : 0)
<< "-" << month << "-" << day << std::endl;
return *this;
}
move
將一個(gè)左值強(qiáng)制轉(zhuǎn)換為右值引用,從而可以調(diào)用移動(dòng)語(yǔ)義。
Date d1(1,2,3); Date d2= std::move(d1); // 調(diào)用移動(dòng)構(gòu)造函數(shù) // 注意:d1的資源已被轉(zhuǎn)移,此時(shí)d1處于有效但未指定的狀態(tài)
完美轉(zhuǎn)發(fā)
完美轉(zhuǎn)發(fā)允許函數(shù)模板將參數(shù)以原始的值類別轉(zhuǎn)發(fā)給其他函數(shù),避免不必要的拷貝或轉(zhuǎn)換。
萬能引用與forward
在模板中,使用T&&聲明的參數(shù)可以綁定到任何類型的參數(shù)(左值或右值)。forward在轉(zhuǎn)發(fā)過程中保持參數(shù)的原始值類別。
template<typename T>
void func1(T&& arg) {
// 如果arg是右值,轉(zhuǎn)發(fā)為右值;如果是左值,轉(zhuǎn)發(fā)為左值
func2(std::forward<T>(arg));
}
// 示例調(diào)用
void func(int& x) { std::cout << "左值" << std::endl; }
void func(int&& x) { std::cout << "右值" << std::endl; }
int main() {
int x = 10;
func1(x); // 轉(zhuǎn)發(fā)為左值,調(diào)用func(int&)
func1(20); // 轉(zhuǎn)發(fā)為右值,調(diào)用func(int&&)
return 0;
}
完整代碼:
class Date {
public:
// 默認(rèn)構(gòu)造函數(shù)
Date(int y = 2000, int m = 1, int d = 1)
: year(new int(y)), month(m), day(d) {
std::cout << "構(gòu)造: " << *year << "-" << month << "-" << day << std::endl;
}
// 拷貝構(gòu)造函數(shù)
Date(const Date& other)
: year(new int(*other.year)), month(other.month), day(other.day) {
std::cout << "拷貝構(gòu)造: " << *year << "-" << month << "-" << day << std::endl;
}
// 釋放原對(duì)象的資源,防止析構(gòu)時(shí)重復(fù)釋放
Date(Date&& other) noexcept
: year(other.year), month(other.month), day(other.day) {
other.year = nullptr; // 轉(zhuǎn)移資源所有權(quán)
std::cout << "移動(dòng)構(gòu)造: " << (year ? *year : 0) << "-" << month << "-" << day << std::endl;
}
// 拷貝賦值運(yùn)算符
Date& operator=(const Date& other) {
if (this != &other) {
delete year;
year = new int(*other.year);
month = other.month;
day = other.day;
}
std::cout << "拷貝賦值: " << *year << "-" << month << "-" << day << std::endl;
return *this;
}
// 移動(dòng)賦值運(yùn)算符
Date& operator=(Date&& other) noexcept {
if (this != &other) {
delete year; // 釋放當(dāng)前資源
year = other.year; // 轉(zhuǎn)移資源
month = other.month;
day = other.day;
other.year = nullptr; // 重置源對(duì)象
}
std::cout << "移動(dòng)賦值: " << (year ? *year : 0) << "-" << month << "-" << day << std::endl;
return *this;
}
// 析構(gòu)函數(shù)
~Date() {
delete year;
std::cout << "析構(gòu): " << (year ? *year : 0) << "-" << month << "-" << day << std::endl;
}
// 獲取日期字符串
std::string toString() const {
return year ?
std::to_string(*year) + "-" + std::to_string(month) + "-" + std::to_string(day) :
"invalid date";
}
private:
int* year; // 動(dòng)態(tài)分配資源
int month;
int day;
};
// 完美轉(zhuǎn)發(fā)示例
template<typename T>
void setDate(T&& date) {
Date storage = std::forward<T>(date); // 保持原始值類別
std::cout << "存儲(chǔ)日期: " << storage.toString() << std::endl;
}
int main() {
// 移動(dòng)構(gòu)造示例
Date d1(2025, 10, 1);
Date d2(std::move(d1)); // 調(diào)用移動(dòng)構(gòu)造函數(shù)
std::cout << "d1: " << d1.toString() << std::endl; // 輸出: invalid date
std::cout << "d2: " << d2.toString() << std::endl; // 輸出: 2023-10-1
// 移動(dòng)賦值示例
Date d3(2025, 1, 1);
d2 = std::move(d3); // 調(diào)用移動(dòng)賦值運(yùn)算符
std::cout << "d2: " << d2.toString() << std::endl; // 輸出: 2024-1-1
std::cout << "d3: " << d3.toString() << std::endl; // 輸出: invalid date
// 完美轉(zhuǎn)發(fā)示例
Date d4(2025, 5, 15);
setDate(d4); // 左值參數(shù),調(diào)用拷貝構(gòu)造
setDate(Date(2025, 10, 1)); // 右值參數(shù),調(diào)用移動(dòng)構(gòu)造
return 0;
}
Lambda 表達(dá)式詳解
隨著C++語(yǔ)法的發(fā)展,人們開始覺得上面的寫法太復(fù)雜了,每次為了實(shí)現(xiàn)一個(gè)algorithm算法, 都要重新去寫一個(gè)類,如果每次比較的邏輯不一樣,還要去實(shí)現(xiàn)多個(gè)類,特別是相同類的命名,這些都給編程者帶來了極大的不便。因此,在C++11語(yǔ)法中出現(xiàn)了Lambda表達(dá)式。
Lambda 表達(dá)式
int main()
{
vector<Goods> v = { { "蘋果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,
3 }, { "菠蘿", 1.5, 4 } };
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){
return g1._price < g2._price; });
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){
return g1._price > g2._price; });
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){
return g1._evaluate < g2._evaluate; });
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){
return g1._evaluate > g2._evaluate; });
}
上述代碼就是使用C++11中的lambda表達(dá)式來解決,可以看出lambda表達(dá)式實(shí)際是一個(gè)匿名函數(shù)。
Lambda 表達(dá)式語(yǔ)法
[capture-list](parameters) mutable -> return-type { body }
- 捕獲列表 (capture-list):定義外部變量的訪問方式
- 參數(shù)列表 (parameters):與普通函數(shù)參數(shù)類似
- mutable 修飾符:允許修改按值捕獲的變量
- 返回類型 (return-type):可顯式聲明或自動(dòng)推導(dǎo)
- 函數(shù)體 (body):包含實(shí)際執(zhí)行的代碼
注意:
在lambda函數(shù)定義中,參數(shù)列表和返回值類型都是可選部分,而捕捉列表和函數(shù)體可以為空。因此C++11中最簡(jiǎn)單的lambda函數(shù)為:[]{}; 該lambda函數(shù)不能做任何事情。
int main()
{
// 最簡(jiǎn)單的lambda表達(dá)式, 該lambda表達(dá)式?jīng)]有任何意義
[]{};
// 省略參數(shù)列表和返回值類型,返回值類型由編譯器推導(dǎo)為int
int a = 3, b = 4;
[=]{return a + 3; };
// 省略了返回值類型,無返回值類型
auto fun1 = [&](int c){b = a + c; };
fun1(10)
cout<<a<<" "<<b<<endl;
}
通過上述例子可以看出,lambda表達(dá)式實(shí)際上可以理解為無名函數(shù),該函數(shù)無法直接調(diào)用,如果想要直接調(diào)用,可借助auto將其賦值給一個(gè)變量。
捕獲列表說明
捕捉列表描述了上下文中那些數(shù)據(jù)可以被lambda使用,以及使用的方式傳值還是傳引用。
- [var]:表示值傳遞方式捕捉變量var
- [=]:表示值傳遞方式捕獲所有父作用域中的變量(包括this)
- [&var]:表示引用傳遞捕捉變量var
- [&]:表示引用傳遞捕捉所有父作用域中的變量(包括this)
- [this]:表示值傳遞方式捕捉當(dāng)前的this指針
注意:
父作用域定義
- 指包含 lambda 函數(shù)的語(yǔ)句塊(局部作用域)
捕獲列表語(yǔ)法
- 多捕獲項(xiàng)用逗號(hào)分隔
禁止重復(fù)捕獲
- 非局部作用域限制
- 全局/命名空間作用域的 lambda 捕獲列表必須為空
局部變量捕獲限制
- 只能捕獲父作用域的局部變量或
this - 禁止捕獲類成員變量(除非通過
this)
Lambda對(duì)象唯一性
- 每個(gè)lambda表達(dá)式生成唯一類型
- 禁止同類型lambda相互賦值(即使捕獲方式相同)
函數(shù)對(duì)象與lambda表達(dá)式
函數(shù)對(duì)象,又稱為仿函數(shù),即可以想函數(shù)一樣使用的對(duì)象,就是在類中重載了operator()運(yùn)算符的類對(duì)象。
class Rate{
public:
Rate(double rate): _rate(rate){}
double operator()(double money, int year){
return money * _rate * year;}
private:
double _rate;
};
int main(){
// 函數(shù)對(duì)象
double rate = 0.49;
Rate r1(rate);
r1(10000, 2);
// lamber
auto r2 = [=](double monty, int year)->double{return monty*rate*year;
};
r2(10000, 2);
return 0
從使用方式上來看,函數(shù)對(duì)象與lambda表達(dá)式完全一樣。
函數(shù)對(duì)象將rate作為其成員變量,在定義對(duì)象時(shí)給出初始值即可,lambda表達(dá)式通過捕獲列表可以直接將該變量捕獲到。

總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
C++常用函數(shù)總結(jié)(algorithm 頭文件)
本文給大家詳細(xì)介紹了algorithm 頭文件中最常用的函數(shù)及其使用方法,當(dāng)然這只是其中的一部分,algorithm 頭文件中還有很多其他的函數(shù),感興趣的朋友一起看看吧2023-12-12
C++類和對(duì)象之類的6個(gè)默認(rèn)成員函數(shù)詳解
類是對(duì)某一事物的抽象描述,具體地講類是C++中的一種構(gòu)造的數(shù)據(jù)類型,下面這篇文章主要給大家介紹了關(guān)于C++類和對(duì)象之類的6個(gè)默認(rèn)成員函數(shù)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02
C語(yǔ)言中的浮點(diǎn)數(shù)存儲(chǔ)詳解
這篇文章主要介紹了C語(yǔ)言中的浮點(diǎn)數(shù)存儲(chǔ)詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-03-03
C語(yǔ)言 數(shù)據(jù)結(jié)構(gòu)中求解迷宮問題實(shí)現(xiàn)方法
這篇文章主要介紹了C語(yǔ)言 數(shù)據(jù)結(jié)構(gòu)中求解迷宮問題實(shí)現(xiàn)方法的相關(guān)資料,需要的朋友可以參考下2017-03-03
Visual Studio調(diào)試C/C++教程指南
VisualStudio是微軟開發(fā)的一款集成開發(fā)環(huán)境軟件,本文主要介紹了Visual Studio調(diào)試C/C++教程指南,熟悉地掌握基于VS的C/C++調(diào)試技術(shù),可以大幅提升調(diào)試性能,感興趣的可以了解一下2024-06-06
C++實(shí)現(xiàn)基于控制臺(tái)界面的吃豆子游戲
這篇文章主要介紹了C++實(shí)現(xiàn)基于控制臺(tái)界面的吃豆子游戲,實(shí)例分析了吃豆子游戲的原理與C++實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04

